Browse Source

better side-effect detection

better-aggressive
Rich-Harris 9 years ago
parent
commit
d3979bd690
  1. 11
      src/Bundle.js
  2. 213
      src/Declaration.js
  3. 39
      src/ExternalModule.js
  4. 132
      src/Module.js
  5. 107
      src/Statement.js
  6. 16
      src/ast/flatten.js
  7. 4
      src/ast/modifierNodes.js
  8. 92
      src/utils/testForSideEffects.js
  9. 30
      test/form/namespace-optimization-b/_expected/amd.js
  10. 24
      test/form/namespace-optimization-b/_expected/cjs.js
  11. 22
      test/form/namespace-optimization-b/_expected/es6.js
  12. 30
      test/form/namespace-optimization-b/_expected/iife.js
  13. 32
      test/form/namespace-optimization-b/_expected/umd.js
  14. 2
      test/form/self-contained-bundle/_expected/amd.js
  15. 2
      test/form/self-contained-bundle/_expected/cjs.js
  16. 2
      test/form/self-contained-bundle/_expected/es6.js
  17. 2
      test/form/self-contained-bundle/_expected/iife.js
  18. 2
      test/form/self-contained-bundle/_expected/umd.js
  19. 2
      test/form/self-contained-bundle/foo.js
  20. 6
      test/form/side-effect-b/_config.js
  21. 7
      test/form/side-effect-b/_expected/amd.js
  22. 5
      test/form/side-effect-b/_expected/cjs.js
  23. 3
      test/form/side-effect-b/_expected/es6.js
  24. 7
      test/form/side-effect-b/_expected/iife.js
  25. 11
      test/form/side-effect-b/_expected/umd.js
  26. 8
      test/form/side-effect-b/main.js
  27. 6
      test/form/side-effect-c/_config.js
  28. 7
      test/form/side-effect-c/_expected/amd.js
  29. 5
      test/form/side-effect-c/_expected/cjs.js
  30. 3
      test/form/side-effect-c/_expected/es6.js
  31. 7
      test/form/side-effect-c/_expected/iife.js
  32. 11
      test/form/side-effect-c/_expected/umd.js
  33. 10
      test/form/side-effect-c/main.js
  34. 6
      test/form/side-effect-d/_config.js
  35. 7
      test/form/side-effect-d/_expected/amd.js
  36. 5
      test/form/side-effect-d/_expected/cjs.js
  37. 3
      test/form/side-effect-d/_expected/es6.js
  38. 7
      test/form/side-effect-d/_expected/iife.js
  39. 11
      test/form/side-effect-d/_expected/umd.js
  40. 4
      test/form/side-effect-d/main.js
  41. 0
      test/form/side-effect/_config.js
  42. 0
      test/form/side-effect/_expected/amd.js
  43. 0
      test/form/side-effect/_expected/cjs.js
  44. 0
      test/form/side-effect/_expected/es6.js
  45. 0
      test/form/side-effect/_expected/iife.js
  46. 0
      test/form/side-effect/_expected/umd.js
  47. 0
      test/form/side-effect/foo.js
  48. 0
      test/form/side-effect/main.js
  49. 2
      test/form/unmodified-default-exports-function-argument/_expected/amd.js
  50. 2
      test/form/unmodified-default-exports-function-argument/_expected/cjs.js
  51. 2
      test/form/unmodified-default-exports-function-argument/_expected/es6.js
  52. 2
      test/form/unmodified-default-exports-function-argument/_expected/iife.js
  53. 2
      test/form/unmodified-default-exports-function-argument/_expected/umd.js
  54. 2
      test/form/unmodified-default-exports-function-argument/main.js

11
src/Bundle.js

@ -73,14 +73,9 @@ export default class Bundle {
declaration.use();
});
let settled = false;
while ( !settled ) {
settled = true;
this.modules.forEach( module => {
if ( module.markAllSideEffects() ) settled = false;
});
}
this.modules.forEach( module => {
module.markAllSideEffects();
});
this.orderedModules = this.sort();
this.deconflict();

213
src/Declaration.js

@ -1,10 +1,5 @@
import { walk } from 'estree-walker';
import { keys } from './utils/object';
const modifierNodes = {
AssignmentExpression: 'left',
UpdateExpression: 'argument'
};
import { blank, keys } from './utils/object.js';
import testForSideEffects from './utils/testForSideEffects.js';
export default class Declaration {
constructor ( node ) {
@ -22,6 +17,7 @@ export default class Declaration {
this.name = null;
this.isReassigned = false;
this.mutations = [];
this.aliases = [];
}
@ -34,54 +30,195 @@ export default class Declaration {
this.name = reference.name; // TODO handle differences of opinion
if ( reference.isReassignment ) this.isReassigned = true;
if ( reference.isMutation && !~this.mutations.indexOf( reference.statement ) ) {
this.mutations.push( reference.statement );
}
}
mutates () {
// returns a list of things this function mutates when it gets called
if ( !this._mutates ) {
let mutatedNames = {};
hasSideEffect () {
return testForSideEffects( this.functionBody, this.statement.scope, this.statement );
}
const statement = this.statement;
let scope = statement.scope;
render ( es6 ) {
if ( es6 ) return this.name;
if ( !this.isReassigned || !this.isExported ) return this.name;
const addNode = node => {
while ( node.type === 'MemberExpression' ) node = node.object;
if ( node.type === 'Identifier' ) mutatedNames[ node.name ] = true;
};
return `exports.${this.name}`;
}
walk( this.functionBody, {
enter ( node ) {
if ( node._scope ) scope = node._scope;
use () {
this.isUsed = true;
if ( this.statement ) this.statement.mark();
if ( node.type in modifierNodes ) {
addNode( node[ modifierNodes[ node.type ] ] );
} else if ( node.type === 'CallExpression' ) {
addNode( node.callee );
}
},
this.aliases.forEach( alias => alias.use() );
}
}
leave ( node ) {
if ( node._scope ) scope = scope.parent;
}
});
export class SyntheticDefaultDeclaration {
constructor ( node, statement, name ) {
this.node = node;
this.statement = statement;
this.name = name;
this._mutates = keys( mutatedNames );
}
this.original = null;
this.isExported = false;
this.aliases = [];
}
return this._mutates;
addAlias ( declaration ) {
this.aliases.push( declaration );
}
render ( es6 ) {
if ( es6 ) return this.name;
if ( !this.isReassigned || !this.isExported ) return this.name;
addReference ( reference ) {
// Don't change the name to `default`; it's not a valid identifier name.
if ( reference.name === 'default' ) return;
return `exports.${this.name}`;
reference.declaration = this;
this.name = reference.name;
}
bind ( declaration ) {
this.original = declaration;
}
hasSideEffect () {
if ( this.original ) {
return this.original.hasSideEffect();
}
if ( /FunctionExpression/.test( this.node.declaration.type ) ) {
return testForSideEffects( this.node.declaration.body, this.statement.scope, this.statement );
}
}
render () {
return !this.original || this.original.isReassigned ?
this.name :
this.original.render();
}
use () {
this.isUsed = true;
if ( this.statement ) this.statement.mark();
this.statement.mark();
if ( this.original ) this.original.use();
this.aliases.forEach( alias => alias.use() );
}
}
export class SyntheticNamespaceDeclaration {
constructor ( module ) {
this.module = module;
this.name = null;
this.needsNamespaceBlock = false;
this.aliases = [];
this.originals = blank();
module.getExports().forEach( name => {
this.originals[ name ] = module.traceExport( name );
});
}
addAlias ( declaration ) {
this.aliases.push( declaration );
}
addReference ( reference ) {
// if we have e.g. `foo.bar`, we can optimise
// the reference by pointing directly to `bar`
if ( reference.parts.length ) {
reference.name = reference.parts.shift();
reference.end += reference.name.length + 1; // TODO this is brittle
const original = this.originals[ reference.name ];
// throw with an informative error message if the reference doesn't exist.
if ( !original ) {
this.module.bundle.onwarn( `Export '${reference.name}' is not defined by '${this.module.id}'` );
reference.isUndefined = true;
return;
}
original.addReference( reference );
return;
}
// otherwise we're accessing the namespace directly,
// which means we need to mark all of this module's
// exports and render a namespace block in the bundle
if ( !this.needsNamespaceBlock ) {
this.needsNamespaceBlock = true;
this.module.bundle.internalNamespaces.push( this );
}
reference.declaration = this;
this.name = reference.name;
}
renderBlock ( indentString ) {
const members = keys( this.originals ).map( name => {
const original = this.originals[ name ];
if ( original.isReassigned ) {
return `${indentString}get ${name} () { return ${original.render()}; }`;
}
return `${indentString}${name}: ${original.render()}`;
});
return `var ${this.render()} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`;
}
render () {
return this.name;
}
use () {
keys( this.originals ).forEach( name => {
this.originals[ name ].use();
});
this.aliases.forEach( alias => alias.use() );
}
}
export class ExternalDeclaration {
constructor ( module, name ) {
this.module = module;
this.name = name;
this.isExternal = true;
}
addAlias () {
// noop
}
addReference ( reference ) {
reference.declaration = this;
if ( this.name === 'default' || this.name === '*' ) {
this.module.suggestName( reference.name );
}
}
render ( es6 ) {
if ( this.name === '*' ) {
return this.module.name;
}
if ( this.name === 'default' ) {
return !es6 && this.module.exportsNames ?
`${this.module.name}__default` :
this.module.name;
}
return es6 ? this.name : `${this.module.name}.${this.name}`;
}
use () {
// noop?
}
}

39
src/ExternalModule.js

@ -1,43 +1,6 @@
import { blank } from './utils/object.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
class ExternalDeclaration {
constructor ( module, name ) {
this.module = module;
this.name = name;
this.isExternal = true;
}
addAlias () {
// noop
}
addReference ( reference ) {
reference.declaration = this;
if ( this.name === 'default' || this.name === '*' ) {
this.module.suggestName( reference.name );
}
}
render ( es6 ) {
if ( this.name === '*' ) {
return this.module.name;
}
if ( this.name === 'default' ) {
return !es6 && this.module.exportsNames ?
`${this.module.name}__default` :
this.module.name;
}
return es6 ? this.name : `${this.module.name}.${this.name}`;
}
use () {
// noop?
}
}
import { ExternalDeclaration } from './Declaration.js';
export default class ExternalModule {
constructor ( id ) {

132
src/Module.js

@ -7,131 +7,7 @@ import { basename, extname } from './utils/path.js';
import getLocation from './utils/getLocation.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js';
class SyntheticDefaultDeclaration {
constructor ( node, statement, name ) {
this.node = node;
this.statement = statement;
this.name = name;
this.original = null;
this.isExported = false;
this.aliases = [];
}
addAlias ( declaration ) {
this.aliases.push( declaration );
}
addReference ( reference ) {
// Don't change the name to `default`; it's not a valid identifier name.
if ( reference.name === 'default' ) return;
reference.declaration = this;
this.name = reference.name;
}
bind ( declaration ) {
this.original = declaration;
}
mutates () {
return this.original.mutates();
}
render () {
return !this.original || this.original.isReassigned ?
this.name :
this.original.render();
}
use () {
this.isUsed = true;
this.statement.mark();
if ( this.original ) this.original.use();
this.aliases.forEach( alias => alias.use() );
}
}
class SyntheticNamespaceDeclaration {
constructor ( module ) {
this.module = module;
this.name = null;
this.needsNamespaceBlock = false;
this.aliases = [];
this.originals = blank();
module.getExports().forEach( name => {
this.originals[ name ] = module.traceExport( name );
});
}
addAlias ( declaration ) {
this.aliases.push( declaration );
}
addReference ( reference ) {
// if we have e.g. `foo.bar`, we can optimise
// the reference by pointing directly to `bar`
if ( reference.parts.length ) {
reference.name = reference.parts.shift();
reference.end += reference.name.length + 1; // TODO this is brittle
const original = this.originals[ reference.name ];
// throw with an informative error message if the reference doesn't exist.
if ( !original ) {
this.module.bundle.onwarn( `Export '${reference.name}' is not defined by '${this.module.id}'` );
reference.isUndefined = true;
return;
}
original.addReference( reference );
return;
}
// otherwise we're accessing the namespace directly,
// which means we need to mark all of this module's
// exports and render a namespace block in the bundle
if ( !this.needsNamespaceBlock ) {
this.needsNamespaceBlock = true;
this.module.bundle.internalNamespaces.push( this );
}
reference.declaration = this;
this.name = reference.name;
}
renderBlock ( indentString ) {
const members = keys( this.originals ).map( name => {
const original = this.originals[ name ];
if ( original.isReassigned ) {
return `${indentString}get ${name} () { return ${original.render()}; }`;
}
return `${indentString}${name}: ${original.render()}`;
});
return `var ${this.render()} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`;
}
render () {
return this.name;
}
use () {
keys( this.originals ).forEach( name => {
this.originals[ name ].use();
});
this.aliases.forEach( alias => alias.use() );
}
}
import { SyntheticDefaultDeclaration, SyntheticNamespaceDeclaration } from './Declaration.js';
export default class Module {
constructor ({ id, code, originalCode, ast, sourceMapChain, bundle }) {
@ -411,13 +287,9 @@ export default class Module {
}
markAllSideEffects () {
let hasSideEffect = false;
this.statements.forEach( statement => {
if ( statement.markSideEffect() ) hasSideEffect = true;
statement.markSideEffect();
});
return hasSideEffect;
}
namespace () {

107
src/Statement.js

@ -1,16 +1,9 @@
import { walk } from 'estree-walker';
import Scope from './ast/Scope.js';
import attachScopes from './ast/attachScopes.js';
import modifierNodes from './ast/modifierNodes.js';
import getLocation from './utils/getLocation.js';
const modifierNodes = {
AssignmentExpression: 'left',
UpdateExpression: 'argument'
};
function isIife ( node, parent ) {
return parent && parent.type === 'CallExpression' && node === parent.callee;
}
import testForSideEffects from './utils/testForSideEffects.js';
function isReference ( node, parent ) {
if ( node.type === 'MemberExpression' ) {
@ -35,9 +28,10 @@ function isReference ( node, parent ) {
}
class Reference {
constructor ( node, scope ) {
constructor ( node, scope, statement ) {
this.node = node;
this.scope = scope;
this.statement = statement;
this.declaration = null; // bound later
@ -90,6 +84,7 @@ export default class Statement {
});
// find references
const statement = this;
let { module, references, scope, stringLiteralRanges } = this;
let readDepth = 0;
@ -106,7 +101,7 @@ export default class Statement {
}
if ( node._scope ) scope = node._scope;
if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth += 1;
if ( /Function/.test( node.type ) ) readDepth += 1;
// special case – shorthand properties. because node.key === node.value,
// we can't differentiate once we've descended into the node
@ -117,9 +112,10 @@ export default class Statement {
return this.skip();
}
const isMutation = parent && parent.type in modifierNodes;
let isReassignment;
if ( parent && parent.type in modifierNodes ) {
if ( isMutation ) {
let subject = parent[ modifierNodes[ parent.type ] ];
let depth = 0;
@ -153,18 +149,19 @@ export default class Statement {
scope.parent :
scope;
const reference = new Reference( node, referenceScope );
const reference = new Reference( node, referenceScope, statement );
references.push( reference );
reference.isImmediatelyUsed = !readDepth;
reference.isReassignment = isReassignment;
reference.isMutation = !readDepth && isMutation;
this.skip(); // don't descend from `foo.bar.baz` into `foo.bar`
}
},
leave ( node, parent ) {
leave ( node ) {
if ( node._scope ) scope = scope.parent;
if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth -= 1;
if ( /Function/.test( node.type ) ) readDepth -= 1;
}
});
}
@ -181,82 +178,10 @@ export default class Statement {
markSideEffect () {
if ( this.isIncluded ) return;
const statement = this;
let hasSideEffect = false;
walk( this.node, {
enter ( node ) {
// Don't descend into (quasi) function declarations – we'll worry about those
// if they get called
if ( node.type === 'FunctionDeclaration' || node.type === 'VariableDeclarator' && node.init && /FunctionExpression/.test( node.init.type ) ) {
return this.skip();
}
// 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;
}
else if ( node.type === 'CallExpression' ) {
if ( node.callee.type === 'Identifier' ) {
const declaration = statement.module.trace( node.callee.name );
if ( !declaration || !declaration.isFunctionDeclaration ) {
hasSideEffect = true;
}
else {
const mutatedByFunction = declaration.mutates();
let i = mutatedByFunction.length;
while ( i-- ) {
const mutatedDeclaration = statement.module.trace( mutatedByFunction[i] );
if ( !mutatedDeclaration || mutatedDeclaration.isUsed ) {
hasSideEffect = true;
break;
}
}
// if ( declaration.hasSideEffect() ) {
// // if calling this function creates side-effects...
// hasSideEffect = true;
// }
//
// else {
// // ...or mutates inputs that are included...
// hasSideEffect = true;
// }
// TODO does function mutate inputs that are needed?
}
}
else if ( node.callee.type === 'MemberExpression' ) {
// if we're calling e.g. Object.keys(thing), there are no side-effects
// TODO
hasSideEffect = true;
}
}
else 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;
}
}
if ( hasSideEffect ) this.skip();
}
});
if ( hasSideEffect ) statement.mark();
return hasSideEffect;
if ( testForSideEffects( this.node, this.scope, this ) ) {
this.mark();
return true;
}
}
source () {

16
src/ast/flatten.js

@ -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( '.' ) };
}

4
src/ast/modifierNodes.js

@ -0,0 +1,4 @@
export default {
AssignmentExpression: 'left',
UpdateExpression: 'argument'
};

92
src/utils/testForSideEffects.js

@ -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;
}

30
test/form/namespace-optimization-b/_expected/amd.js

@ -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();
});

24
test/form/namespace-optimization-b/_expected/cjs.js

@ -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();

22
test/form/namespace-optimization-b/_expected/es6.js

@ -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();

30
test/form/namespace-optimization-b/_expected/iife.js

@ -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();
})();

32
test/form/namespace-optimization-b/_expected/umd.js

@ -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();
}));

2
test/form/self-contained-bundle/_expected/amd.js

@ -1,7 +1,7 @@
define(function () { 'use strict';
function foo () {
return bar();
console.log( bar() );
}
function bar () {

2
test/form/self-contained-bundle/_expected/cjs.js

@ -1,7 +1,7 @@
'use strict';
function foo () {
return bar();
console.log( bar() );
}
function bar () {

2
test/form/self-contained-bundle/_expected/es6.js

@ -1,5 +1,5 @@
function foo () {
return bar();
console.log( bar() );
}
function bar () {

2
test/form/self-contained-bundle/_expected/iife.js

@ -1,7 +1,7 @@
(function () { 'use strict';
function foo () {
return bar();
console.log( bar() );
}
function bar () {

2
test/form/self-contained-bundle/_expected/umd.js

@ -5,7 +5,7 @@
}(this, function () { 'use strict';
function foo () {
return bar();
console.log( bar() );
}
function bar () {

2
test/form/self-contained-bundle/foo.js

@ -1,5 +1,5 @@
export default function foo () {
return bar();
console.log( bar() );
}
function bar () {

6
test/form/side-effect-b/_config.js

@ -0,0 +1,6 @@
module.exports = {
description: 'discards IIFE with no side-effects',
options: {
moduleName: 'myBundle'
}
};

7
test/form/side-effect-b/_expected/amd.js

@ -0,0 +1,7 @@
define(function () { 'use strict';
var main = 42;
return main;
});

5
test/form/side-effect-b/_expected/cjs.js

@ -0,0 +1,5 @@
'use strict';
var main = 42;
module.exports = main;

3
test/form/side-effect-b/_expected/es6.js

@ -0,0 +1,3 @@
var main = 42;
export default main;

7
test/form/side-effect-b/_expected/iife.js

@ -0,0 +1,7 @@
var myBundle = (function () { 'use strict';
var main = 42;
return main;
})();

11
test/form/side-effect-b/_expected/umd.js

@ -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;
}));

8
test/form/side-effect-b/main.js

@ -0,0 +1,8 @@
var Unused = (function () {
function Unused () {}
Unused.prototype = {};
return Unused;
}());
export default 42;

6
test/form/side-effect-c/_config.js

@ -0,0 +1,6 @@
module.exports = {
description: 'discards function with no side-effects',
options: {
moduleName: 'myBundle'
}
};

7
test/form/side-effect-c/_expected/amd.js

@ -0,0 +1,7 @@
define(function () { 'use strict';
var main = 42;
return main;
});

5
test/form/side-effect-c/_expected/cjs.js

@ -0,0 +1,5 @@
'use strict';
var main = 42;
module.exports = main;

3
test/form/side-effect-c/_expected/es6.js

@ -0,0 +1,3 @@
var main = 42;
export default main;

7
test/form/side-effect-c/_expected/iife.js

@ -0,0 +1,7 @@
var myBundle = (function () { 'use strict';
var main = 42;
return main;
})();

11
test/form/side-effect-c/_expected/umd.js

@ -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;
}));

10
test/form/side-effect-c/main.js

@ -0,0 +1,10 @@
var factory = function () {
function Unused () {}
Unused.prototype = {};
return Unused;
};
var Unused = factory();
export default 42;

6
test/form/side-effect-d/_config.js

@ -0,0 +1,6 @@
module.exports = {
description: 'excludes functions that are known to be pure',
options: {
moduleName: 'myBundle'
}
};

7
test/form/side-effect-d/_expected/amd.js

@ -0,0 +1,7 @@
define(function () { 'use strict';
var main = 42;
return main;
});

5
test/form/side-effect-d/_expected/cjs.js

@ -0,0 +1,5 @@
'use strict';
var main = 42;
module.exports = main;

3
test/form/side-effect-d/_expected/es6.js

@ -0,0 +1,3 @@
var main = 42;
export default main;

7
test/form/side-effect-d/_expected/iife.js

@ -0,0 +1,7 @@
var myBundle = (function () { 'use strict';
var main = 42;
return main;
})();

11
test/form/side-effect-d/_expected/umd.js

@ -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;
}));

4
test/form/side-effect-d/main.js

@ -0,0 +1,4 @@
var obj = { foo: 1, bar: 2 };
var keys = Object.keys( obj );
export default 42;

0
test/form/unused-side-effect/_config.js → test/form/side-effect/_config.js

0
test/form/unused-side-effect/_expected/amd.js → test/form/side-effect/_expected/amd.js

0
test/form/unused-side-effect/_expected/cjs.js → test/form/side-effect/_expected/cjs.js

0
test/form/unused-side-effect/_expected/es6.js → test/form/side-effect/_expected/es6.js

0
test/form/unused-side-effect/_expected/iife.js → test/form/side-effect/_expected/iife.js

0
test/form/unused-side-effect/_expected/umd.js → test/form/side-effect/_expected/umd.js

0
test/form/unused-side-effect/foo.js → test/form/side-effect/foo.js

0
test/form/unused-side-effect/main.js → test/form/side-effect/main.js

2
test/form/unmodified-default-exports-function-argument/_expected/amd.js

@ -11,4 +11,6 @@ define(function () { 'use strict';
var answer = foo();
var somethingElse = bar();
console.log( answer );
});

2
test/form/unmodified-default-exports-function-argument/_expected/cjs.js

@ -10,3 +10,5 @@ function bar () {
var answer = foo();
var somethingElse = bar();
console.log( answer );

2
test/form/unmodified-default-exports-function-argument/_expected/es6.js

@ -8,3 +8,5 @@ function bar () {
var answer = foo();
var somethingElse = bar();
console.log( answer );

2
test/form/unmodified-default-exports-function-argument/_expected/iife.js

@ -11,4 +11,6 @@
var answer = foo();
var somethingElse = bar();
console.log( answer );
})();

2
test/form/unmodified-default-exports-function-argument/_expected/umd.js

@ -15,4 +15,6 @@
var answer = foo();
var somethingElse = bar();
console.log( answer );
}));

2
test/form/unmodified-default-exports-function-argument/main.js

@ -2,3 +2,5 @@ import foo, { bar } from './foo';
var answer = foo();
var somethingElse = bar();
console.log( answer );

Loading…
Cancel
Save