Browse Source

Merge branch 'gh-92' into gh-91

contingency-plan
Rich-Harris 10 years ago
parent
commit
b5fd2b72aa
  1. 8
      src/Bundle.js
  2. 25
      src/Module.js
  3. 53
      src/Statement.js
  4. 3
      test/form/exclude-unnecessary-modifications/_config.js
  5. 28
      test/form/exclude-unnecessary-modifications/_expected/amd.js
  6. 26
      test/form/exclude-unnecessary-modifications/_expected/cjs.js
  7. 24
      test/form/exclude-unnecessary-modifications/_expected/es6.js
  8. 28
      test/form/exclude-unnecessary-modifications/_expected/iife.js
  9. 32
      test/form/exclude-unnecessary-modifications/_expected/umd.js
  10. 37
      test/form/exclude-unnecessary-modifications/foo.js
  11. 2
      test/form/exclude-unnecessary-modifications/main.js
  12. 6
      test/form/exported-empty-vars/_config.js
  13. 8
      test/form/exported-empty-vars/_expected/amd.js
  14. 6
      test/form/exported-empty-vars/_expected/cjs.js
  15. 9
      test/form/exported-empty-vars/_expected/es6.js
  16. 8
      test/form/exported-empty-vars/_expected/iife.js
  17. 6
      test/form/exported-empty-vars/bar.js
  18. 2
      test/form/exported-empty-vars/foo.js
  19. 2
      test/form/exported-empty-vars/main.js
  20. 7
      test/form/exports-at-end-if-possible/_config.js
  21. 11
      test/form/exports-at-end-if-possible/_expected/amd.js
  22. 9
      test/form/exports-at-end-if-possible/_expected/cjs.js
  23. 7
      test/form/exports-at-end-if-possible/_expected/es6.js
  24. 11
      test/form/exports-at-end-if-possible/_expected/iife.js
  25. 9
      test/form/exports-at-end-if-possible/_expected/umd.js
  26. 5
      test/form/exports-at-end-if-possible/main.js
  27. 7
      test/form/multiple-exports/_expected/amd.js
  28. 7
      test/form/multiple-exports/_expected/cjs.js
  29. 7
      test/form/multiple-exports/_expected/iife.js
  30. 7
      test/form/multiple-exports/_expected/umd.js
  31. 4
      test/form/preserves-comments-after-imports/_expected/amd.js
  32. 4
      test/form/preserves-comments-after-imports/_expected/cjs.js
  33. 4
      test/form/preserves-comments-after-imports/_expected/iife.js
  34. 4
      test/form/preserves-comments-after-imports/_expected/umd.js
  35. 3
      test/function/assignment-to-exports/_config.js

8
src/Bundle.js

@ -290,13 +290,13 @@ export default class Bundle {
//
// This doesn't apply if the bundle is exported as ES6!
let allBundleExports = blank();
let isVarDeclaration = blank();
let isReassignedVarDeclaration = blank();
let varExports = blank();
let getterExports = [];
this.orderedModules.forEach( module => {
module.varDeclarations.forEach( name => {
isVarDeclaration[ module.replacements[ name ] || name ] = true;
module.reassignments.forEach( name => {
isReassignedVarDeclaration[ module.replacements[ name ] || name ] = true;
});
});
@ -306,7 +306,7 @@ export default class Bundle {
.forEach( name => {
const canonicalName = this.traceExport( this.entryModule, name );
if ( isVarDeclaration[ canonicalName ] ) {
if ( isReassignedVarDeclaration[ canonicalName ] ) {
varExports[ name ] = true;
// if the same binding is exported multiple ways, we need to

25
src/Module.js

@ -64,7 +64,7 @@ export default class Module {
this.replacements = blank();
this.varDeclarations = [];
this.reassignments = [];
this.definitions = blank();
this.definitionPromises = blank();
@ -202,20 +202,35 @@ export default class Module {
this.definitions[ name ] = statement;
});
statement.scope.varDeclarations.forEach( name => {
this.varDeclarations.push( name );
});
keys( statement.modifies ).forEach( name => {
( this.modifications[ name ] || ( this.modifications[ name ] = [] ) ).push( statement );
});
});
// discover variables that are reassigned inside function
// bodies, so we can keep bindings live, e.g.
//
// export var count = 0;
// export function incr () { count += 1 }
let reassigned = blank();
this.statements.forEach( statement => {
keys( statement.reassigns ).forEach( name => {
reassigned[ name ] = true;
});
});
// if names are referenced that are neither defined nor imported
// in this module, we assume that they're globals
this.statements.forEach( statement => {
if ( statement.isReexportDeclaration ) return;
// while we're here, mark reassignments
statement.scope.varDeclarations.forEach( name => {
if ( reassigned[ name ] && !~this.reassignments.indexOf( name ) ) {
this.reassignments.push( name );
}
});
keys( statement.dependsOn ).forEach( name => {
if ( !this.definitions[ name ] && !this.imports[ name ] ) {
this.bundle.assumedGlobals[ name ] = true;

53
src/Statement.js

@ -8,6 +8,14 @@ function isIife ( node, parent ) {
return parent && parent.type === 'CallExpression' && node === parent.callee;
}
function isFunctionDeclaration ( node, parent ) {
// `function foo () {}`
if ( node.type === 'FunctionDeclaration' ) return true;
// `var foo = function () {}` - same thing for present purposes
if ( node.type === 'FunctionExpression' && parent.type === 'VariableDeclarator' ) return true;
}
export default class Statement {
constructor ( node, module, start, end ) {
this.node = node;
@ -22,6 +30,8 @@ export default class Statement {
this.dependsOn = blank();
this.stronglyDependsOn = blank();
this.reassigns = blank();
this.isIncluded = false;
this.isImportDeclaration = node.type === 'ImportDeclaration';
@ -117,22 +127,34 @@ export default class Statement {
// calledImmediately();
//
// ...but it's better than nothing
let depth = 0;
let readDepth = 0;
// This allows us to track whether a modifying statement (i.e. assignment
// /update expressions) need to be captured
let writeDepth = 0;
if ( !this.isImportDeclaration ) {
walk( this.node, {
enter: ( node, parent ) => {
if ( node._scope ) {
if ( !scope.isBlockScope && !isIife( node, parent ) ) depth += 1;
if ( !scope.isBlockScope ) {
if ( !isIife( node, parent ) ) readDepth += 1;
if ( isFunctionDeclaration( node, parent ) ) writeDepth += 1;
}
scope = node._scope;
}
this.checkForReads( scope, node, parent, !depth );
this.checkForWrites( scope, node );
this.checkForReads( scope, node, parent, !readDepth );
this.checkForWrites( scope, node, writeDepth );
},
leave: ( node, parent ) => {
if ( node._scope ) {
if ( !scope.isBlockScope && !isIife( node, parent ) ) depth -= 1;
if ( !scope.isBlockScope ) {
if ( !isIife( node, parent ) ) readDepth -= 1;
if ( isFunctionDeclaration( node, parent ) ) writeDepth -= 1;
}
scope = scope.parent;
}
}
@ -171,7 +193,7 @@ export default class Statement {
}
}
checkForWrites ( scope, node ) {
checkForWrites ( scope, node, writeDepth ) {
const addNode = ( node, isAssignment ) => {
let depth = 0; // determine whether we're illegally modifying a binding or namespace
@ -207,9 +229,26 @@ export default class Statement {
this.module.exports.default.isModified = true;
}
}
// we track updates/reassignments to variables, to know whether we
// need to rewrite it later from `foo` to `exports.foo` to keep
// bindings live
if (
depth === 0 &&
writeDepth > 0 &&
!scope.contains( node.name )
) {
this.reassigns[ node.name ] = true;
}
}
if ( node.type === 'Identifier' ) {
// we only care about writes that happen a) at the top level,
// or b) inside a function that could be immediately invoked.
// Writes inside named functions are only relevant if the
// function is called, in which case we don't need to do
// anything (but we still need to call checkForWrites to
// catch illegal reassignments to imported bindings)
if ( writeDepth === 0 && node.type === 'Identifier' ) {
this.modifies[ node.name ] = true;
}
};

3
test/form/exclude-unnecessary-modifications/_config.js

@ -0,0 +1,3 @@
module.exports = {
description: 'statements that modify definitions within unused functions are excluded'
};

28
test/form/exclude-unnecessary-modifications/_expected/amd.js

@ -0,0 +1,28 @@
define(function () { 'use strict';
var foo = {};
mutate1( foo );
// should be included
[ 'a', 'b', 'c' ].forEach( function ( letter, i ) {
foo[ letter ] = i;
});
[ 'd', 'e', 'f' ].forEach( ( letter, i ) => {
foo[ letter ] = i;
});
function mutate1 () {
foo.mutated = 1;
}
({
mutate2: function () {
foo.mutated = 2;
}
}).mutate2();
console.log( foo );
});

26
test/form/exclude-unnecessary-modifications/_expected/cjs.js

@ -0,0 +1,26 @@
'use strict';
var foo = {};
mutate1( foo );
// should be included
[ 'a', 'b', 'c' ].forEach( function ( letter, i ) {
foo[ letter ] = i;
});
[ 'd', 'e', 'f' ].forEach( ( letter, i ) => {
foo[ letter ] = i;
});
function mutate1 () {
foo.mutated = 1;
}
({
mutate2: function () {
foo.mutated = 2;
}
}).mutate2();
console.log( foo );

24
test/form/exclude-unnecessary-modifications/_expected/es6.js

@ -0,0 +1,24 @@
var foo = {};
mutate1( foo );
// should be included
[ 'a', 'b', 'c' ].forEach( function ( letter, i ) {
foo[ letter ] = i;
});
[ 'd', 'e', 'f' ].forEach( ( letter, i ) => {
foo[ letter ] = i;
});
function mutate1 () {
foo.mutated = 1;
}
({
mutate2: function () {
foo.mutated = 2;
}
}).mutate2();
console.log( foo );

28
test/form/exclude-unnecessary-modifications/_expected/iife.js

@ -0,0 +1,28 @@
(function () { 'use strict';
var foo = {};
mutate1( foo );
// should be included
[ 'a', 'b', 'c' ].forEach( function ( letter, i ) {
foo[ letter ] = i;
});
[ 'd', 'e', 'f' ].forEach( ( letter, i ) => {
foo[ letter ] = i;
});
function mutate1 () {
foo.mutated = 1;
}
({
mutate2: function () {
foo.mutated = 2;
}
}).mutate2();
console.log( foo );
})();

32
test/form/exclude-unnecessary-modifications/_expected/umd.js

@ -0,0 +1,32 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
factory();
}(this, function () { 'use strict';
var foo = {};
mutate1( foo );
// should be included
[ 'a', 'b', 'c' ].forEach( function ( letter, i ) {
foo[ letter ] = i;
});
[ 'd', 'e', 'f' ].forEach( ( letter, i ) => {
foo[ letter ] = i;
});
function mutate1 () {
foo.mutated = 1;
}
({
mutate2: function () {
foo.mutated = 2;
}
}).mutate2();
console.log( foo );
}));

37
test/form/exclude-unnecessary-modifications/foo.js

@ -0,0 +1,37 @@
var foo = {};
mutate1( foo );
// should be included
[ 'a', 'b', 'c' ].forEach( function ( letter, i ) {
foo[ letter ] = i;
});
[ 'd', 'e', 'f' ].forEach( ( letter, i ) => {
foo[ letter ] = i;
});
function mutate1 () {
foo.mutated = 1;
}
({
mutate2: function () {
foo.mutated = 2;
}
}).mutate2();
// should be excluded
var mutate2 = function () {
foo.mutated = 2;
}
function unused1 () {
foo.wat = 'nope';
}
function unused2 () {
mutate1( foo );
}
export default foo;

2
test/form/exclude-unnecessary-modifications/main.js

@ -0,0 +1,2 @@
import foo from './foo';
console.log( foo );

6
test/form/exported-empty-vars/_config.js

@ -1,6 +0,0 @@
module.exports = {
description: 'removes empty var declarations that are exported',
options: {
moduleName: 'myBundle'
}
};

8
test/form/exported-empty-vars/_expected/amd.js

@ -1,8 +0,0 @@
define(['exports'], function (exports) { 'use strict';
exports.foo = 42;
exports.bar = 43;
exports.baz = 44;
});

6
test/form/exported-empty-vars/_expected/cjs.js

@ -1,6 +0,0 @@
'use strict';
exports.foo = 42;
exports.bar = 43;
exports.baz = 44;

9
test/form/exported-empty-vars/_expected/es6.js

@ -1,9 +0,0 @@
var foo;
foo = 42;
var bar;
var baz;
bar = 43;
baz = 44;
export { foo, bar, baz };

8
test/form/exported-empty-vars/_expected/iife.js

@ -1,8 +0,0 @@
(function (exports) { 'use strict';
exports.foo = 42;
exports.bar = 43;
exports.baz = 44;
})((this.myBundle = {}));

6
test/form/exported-empty-vars/bar.js

@ -1,6 +0,0 @@
var bar, baz;
bar = 43;
baz = 44;
export { bar, baz };

2
test/form/exported-empty-vars/foo.js

@ -1,2 +0,0 @@
export var foo;
foo = 42;

2
test/form/exported-empty-vars/main.js

@ -1,2 +0,0 @@
export { foo } from './foo';
export { bar, baz } from './bar';

7
test/form/exports-at-end-if-possible/_config.js

@ -0,0 +1,7 @@
module.exports = {
description: 'exports variables at end, if possible',
options: {
moduleName: 'myBundle'
},
// solo: true
};

11
test/form/exports-at-end-if-possible/_expected/amd.js

@ -0,0 +1,11 @@
define(['exports'], function (exports) { 'use strict';
var FOO = 'foo';
console.log( FOO );
console.log( FOO );
console.log( FOO );
exports.FOO = FOO;
});

9
test/form/exports-at-end-if-possible/_expected/cjs.js

@ -0,0 +1,9 @@
'use strict';
var FOO = 'foo';
console.log( FOO );
console.log( FOO );
console.log( FOO );
exports.FOO = FOO;

7
test/form/exports-at-end-if-possible/_expected/es6.js

@ -0,0 +1,7 @@
var FOO = 'foo';
console.log( FOO );
console.log( FOO );
console.log( FOO );
export { FOO };

11
test/form/exports-at-end-if-possible/_expected/iife.js

@ -0,0 +1,11 @@
(function (exports) { 'use strict';
var FOO = 'foo';
console.log( FOO );
console.log( FOO );
console.log( FOO );
exports.FOO = FOO;
})((this.myBundle = {}));

9
test/form/exported-empty-vars/_expected/umd.js → test/form/exports-at-end-if-possible/_expected/umd.js

@ -4,9 +4,12 @@
factory((global.myBundle = {}));
}(this, function (exports) { 'use strict';
exports.foo = 42;
var FOO = 'foo';
exports.bar = 43;
exports.baz = 44;
console.log( FOO );
console.log( FOO );
console.log( FOO );
exports.FOO = FOO;
}));

5
test/form/exports-at-end-if-possible/main.js

@ -0,0 +1,5 @@
export var FOO = 'foo';
console.log( FOO );
console.log( FOO );
console.log( FOO );

7
test/form/multiple-exports/_expected/amd.js

@ -1,6 +1,9 @@
define(['exports'], function (exports) { 'use strict';
exports.foo = 1;
exports.bar = 2;
var foo = 1;
var bar = 2;
exports.foo = foo;
exports.bar = bar;
});

7
test/form/multiple-exports/_expected/cjs.js

@ -1,4 +1,7 @@
'use strict';
exports.foo = 1;
exports.bar = 2;
var foo = 1;
var bar = 2;
exports.foo = foo;
exports.bar = bar;

7
test/form/multiple-exports/_expected/iife.js

@ -1,6 +1,9 @@
(function (exports) { 'use strict';
exports.foo = 1;
exports.bar = 2;
var foo = 1;
var bar = 2;
exports.foo = foo;
exports.bar = bar;
})((this.myBundle = {}));

7
test/form/multiple-exports/_expected/umd.js

@ -4,7 +4,10 @@
factory((global.myBundle = {}));
}(this, function (exports) { 'use strict';
exports.foo = 1;
exports.bar = 2;
var foo = 1;
var bar = 2;
exports.foo = foo;
exports.bar = bar;
}));

4
test/form/preserves-comments-after-imports/_expected/amd.js

@ -4,6 +4,8 @@ define(['exports'], function (exports) { 'use strict';
var number = 5;
/** A comment for obj */
exports.obj = { number };
var obj = { number };
exports.obj = obj;
});

4
test/form/preserves-comments-after-imports/_expected/cjs.js

@ -4,4 +4,6 @@
var number = 5;
/** A comment for obj */
exports.obj = { number };
var obj = { number };
exports.obj = obj;

4
test/form/preserves-comments-after-imports/_expected/iife.js

@ -4,6 +4,8 @@
var number = 5;
/** A comment for obj */
exports.obj = { number };
var obj = { number };
exports.obj = obj;
})((this.myBundle = {}));

4
test/form/preserves-comments-after-imports/_expected/umd.js

@ -8,6 +8,8 @@
var number = 5;
/** A comment for obj */
exports.obj = { number };
var obj = { number };
exports.obj = obj;
}));

3
test/function/assignment-to-exports/_config.js

@ -6,5 +6,6 @@ module.exports = {
assert.equal( exports.count, 0 );
exports.incr();
assert.equal( exports.count, 1 );
}
},
// solo: true
};

Loading…
Cancel
Save