Skip to content

Commit

Permalink
Fix hoisting of "var" variables on wrapped modules
Browse files Browse the repository at this point in the history
  • Loading branch information
fathyb committed Jul 21, 2018
1 parent 7423541 commit e137c08
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/scope-hoisting/hoist.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@ module.exports = {
) {
scope.push({id: exportsIdentifier, init: t.objectExpression([])});
}

// Move all "var" variables to the top-level to prevent out of order definitions when wrapped.
for (let name in scope.bindings) {
let binding = scope.getBinding(name);

if (binding.path.scope !== scope && binding.kind === 'var') {
let {parentPath} = binding.path;

if (!parentPath.removed) {
if (parentPath.node.declarations.length) {
binding.path.getStatementParent().insertBefore(
parentPath.node.declarations
.map(decl => {
binding.scope.removeBinding(decl.id.name);
scope.push({id: decl.id});

return decl.init
? t.assignmentExpression('=', decl.id, decl.init)
: null;
})
.filter(decl => decl !== null)
);
}

parentPath.remove();
}

binding.path.remove();
}
}
}

path.stop();
Expand Down Expand Up @@ -280,8 +310,9 @@ module.exports = {
}

if (t.isIdentifier(callee, {name: 'require'})) {
let source = args[0].value;
// Ignore require calls that were ignored earlier.
if (!asset.dependencies.has(args[0].value)) {
if (!asset.dependencies.has(source)) {
return;
}

Expand All @@ -296,9 +327,11 @@ module.exports = {
p.isSequenceExpression()
);
if (!parent.isProgram() || bail) {
asset.dependencies.get(args[0].value).shouldWrap = true;
asset.dependencies.get(source).shouldWrap = true;
}

asset.cacheData.imports['$require$' + source] = [source, '*'];

// Generate a variable name based on the current asset id and the module name to require.
// This will be replaced by the final variable name of the resolved asset in the packager.
path.replaceWith(
Expand Down
3 changes: 3 additions & 0 deletions test/integration/scope-hoisting/commonjs/hoist-vars/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const foo = require('./b')

output = foo()
13 changes: 13 additions & 0 deletions test/integration/scope-hoisting/commonjs/hoist-vars/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
if(process.env.NODE_ENV === 'test') {
var c = require('./c')()

for(var i = 0, {length} = c, out = ''; i < length; i++) {
out += c[i]
}

module.exports = function() {
if(c != out) throw new Error()

return out
}
}
12 changes: 12 additions & 0 deletions test/integration/scope-hoisting/commonjs/hoist-vars/c.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = doFoo

function doFoo() {
return foo
}

if(process.env.NODE_ENV === 'test') {
var foo = 'bar'
}
else {
foo = 'foo'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output = require('bar').foo(3)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions test/scope-hoisting.js
Original file line number Diff line number Diff line change
Expand Up @@ -816,5 +816,24 @@ describe('scope hoisting', function() {
let output = await run(b);
assert.deepEqual(output, 3);
});

it('should correctly hoist "var" on wrapped modules', async function() {
let b = await bundle(
__dirname + '/integration/scope-hoisting/commonjs/hoist-vars/a.js'
);

let output = await run(b);
assert.deepEqual(output, 'bar');
});

it('should support sideEffects: false', async function() {
let b = await bundle(
__dirname +
'/integration/scope-hoisting/commonjs/side-effects-false/a.js'
);

let output = await run(b);
assert.deepEqual(output, 9);
});
});
});

0 comments on commit e137c08

Please sign in to comment.