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

plugin-transform-modules-commonjs: export binding not updated by 'for...in' and 'for...of' #10941

Closed
1 task
ajafff opened this issue Dec 29, 2019 · 7 comments · Fixed by #11074
Closed
1 task
Assignees
Labels
area: modules i: bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@ajafff
Copy link
Contributor

ajafff commented Dec 29, 2019

Bug Report

  • I would like to work on a fix!

Current Behavior
If an exported binding is used as LHS in a ForOfStatement or ForInStatement, only the local variable is updated. The exported binding stays the same.

Input Code

export let foo = 'initial';

for (foo in {prop: 1}) {}

for (foo of ['element']) {}

for ({foo} of [{foo: 'destructured'}]) {}

Expected behavior/code
exports.foo should be updated every time foo is assigned a new value.
I tested the code without transpilation in Node.js and it works as expected.

@babel-bot
Copy link
Collaborator

Hey @ajafff! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack community that typically always has someone willing to help. You can sign-up here for an invite."

@vedantroy
Copy link
Contributor

I'll take a shot at this.

@vedantroy
Copy link
Contributor

vedantroy commented Jan 20, 2020

@ajafff How did you test the given code sample in Node.js. I tried running it, but Node.js does not support export let foo = syntax.

I think I'm confused, but it seems like in Node.js, the expected behavior is not for exports.foo to be updated.

For example:

exports.foo = 'initial'

for(foo of ['element']) {
    console.log(foo)
    console.log(exports.foo)
}

Outputs

element
initial

not

element
element

Or are you referring to the fact that if I run the following two files in the browser (without transpilation):

lib.js

export let foo = 1;

export function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

(async () => {
    for (foo of [2, 3, 4, 5, 6, 8, 9, 10]) {
        await sleep(1000)
    }
})()

index.js

import { foo, sleep } from './lib.js'

(async () => {
    for (let i = 0; i < 10; i++) {
        await sleep(1000)
        console.log(foo)
    }
})()

the output will be

1
2
3
...

Whereas, if Babel compiles these files + the files are bundled with a bundler, the output will just be:

1
1
1
...

If so, I tested with a bable + a bundler (parcel), and the output Javascript indicated that exports.foo was being assigned to/updated in the for loop. exports.foo = _arr[_i]

Sidenote, if I set the preset to "es2015", the output seems to update exports.foo inside the for loop. Is your issue that if the preset is not es2015, the output will not be updated?

Output with preset es2015:

for (var _i = 0, _arr = ['element']; _i < _arr.length; _i++) {
  exports.foo = foo = _arr[_i];
}

for (var _i2 = 0, _arr2 = [{
  foo: 'destructured'
}]; _i2 < _arr2.length; _i2++) {
  var _arr2$_i = _arr2[_i2];
  exports.foo = foo = _arr2$_i.foo;
  _arr2$_i;
}

@ajafff
Copy link
Contributor Author

ajafff commented Jan 20, 2020

You can run the untranspiled code in Node.js by using the --experimental-modules option and using .mjs as file extension.
The behavior is the same as running the untranspiled code in the browser.

Since the transpiled code should be semantically identical, it should update exports.foo whenever foo is updated.


I don't know exactly how these presets work. It seems like "es2015" is transpiling the for...of loop to a regular for loop before the commonjs transformer is executed. That already works, because there's an explicit assignment to foo which the commonjs transform is able to find. This bug is only about transpiling to ES6 or higher.

@vedantroy
Copy link
Contributor

vedantroy commented Jan 21, 2020

@ajafff It seems like the solution to this issue, is make Babel replace

for(foo of [1, 2, 3]) {
}

with

for(exports.foo of [1, 2, 3]) {
}

It seems like you've solved this issue in the Typescript compiler, so I was wondering if you have any thoughts on a potential fix.

@ajafff
Copy link
Contributor Author

ajafff commented Jan 21, 2020

@vedantroy while your suggested fix works, there's another case that it doesn't cover: You can export the same variable multiple times with different names:

export let foo;
export {foo as bar};

for (foo of [1, 2]) {}

should give roughly this code:

let foo;
for (foo of [1, 2]) {
  exports.foo = exports.bar = foo;
}

@vedantroy
Copy link
Contributor

vedantroy commented Jan 30, 2020

PR: #11074

@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label May 11, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: modules i: bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants