Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hoisting of "var" variables on wrapped modules #1735

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/scope-hoisting/hoist.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm yeah we do something like this here already: https://github.com/parcel-bundler/parcel/blob/master/src/packagers/JSConcatPackager.js#L282-L319 but it wouldn't get variables inside an if statement like that.

Is there a performance hit to doing this on every module? Should we only do it on wrapped modules (in the packager)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a performance hit to doing this on every module?

I did not found any noticeable change on build time on a medium size app, same for vue-hackernews. We only do it when a var variable is not in the root block of the current scope, which is relatively rare.

Should we only do it on wrapped modules (in the packager)?

I guess you could either :

  • build a scope using babel-traverse and do the same as here => slow
  • do a deep AST traversal => slow and error prone

Just found out other some issues with this PR, and I think I should also do it when path.getData('shouldWrap') == true, I'm putting it in WIP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we don't need to do it when shouldWrap is true, ready to merge!

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
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'
}
9 changes: 9 additions & 0 deletions test/scope-hoisting.js
Original file line number Diff line number Diff line change
Expand Up @@ -842,5 +842,14 @@ describe('scope hoisting', function() {
let output = await run(b);
assert.deepEqual(output, 9);
});

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