-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
ES6 Module mutable exports are not supported #2710
Comments
Slightly related: export from is also broken. Export from requires the above to be fixed and for the export from to use Object.defineProperty with a getter for the reexported property.
|
Might be moot if we ban this. See #2717. |
Another case we'll need to look out for is
|
@ChadKillingsworth I'd like your input on this. I'm thinking the only real way to support this in an efficient manner is to only have globals, which tl;dr breaks So the issue is that we need to use Explanation: Exports can keep being reexported and the reexports reflect the original export. Example:
The only correct way to create an exports object for the second module is like this:
Which is icky and should be avoided. So the idea is if you never use that object (e.g. no
How does this impact most of the CommonJS code you write, if at all? Obviously we'd have to do this rewriting with CommonJS; I'm just not sure if you use the |
And fwiw there'd be an easy work around: rather than |
Given the CommonJS module:
If an ES6 module imported CommonJS, Inside CommonJS, if you import a ES6 module that exported The rewriting you describe is how CommonJS already operates. Here's an example: https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/ProcessCommonJSModulesTest.java#L342-L348 |
Not exactly what I'm talking about. Here's a better example: closure-compiler/test/com/google/javascript/jscomp/CommandLineRunnerTest.java Lines 1780 to 1807 in 3f954c1
Currently:
Proposed solution is that
Note that I'm not proposing changing CommonJS exports. Only references to ES6 module exports, which can exist in a CommonJS module. So what this means is that this works:
This does not, at least for now:
So this means that
I've written a document that hopefully clarifies things as well. |
So if we want to change how ES6 modules are rewritten then that means that I will need to move rewriting of references to ES6 modules via |
Right - sorry I missed some of the nuance until later. This affects rewriting type nodes as well. I had originally thought we should have a shared class that can handle the rewriting of an import. FYI this affects rewriting JSDoc types as well: const Foo = require('/path/to/es6.js');
/**
* @param {!Foo} foo
* @param {!/path/to/es6} foo2
*/
function (foo, foo2) {} |
Right, but why would you ever write a function like that exactly? Is it a common case? |
Two reasons:
|
I don't think it's super common, but I know there are references like that in my code base. I believe there are references like that inside Google as well. |
One more thing: you'll need to have a strategy for per-file transpilation. Right now for CommonJS, if the import module can't be located, I assume that the imported module is the same type (CommonJS). Just need to define this behavior. Per-file transpilation is becoming harder and harder to support. |
I'm still not clear how your example function gets around this. You're importing in your example. imo I thought you didn't use per file transpilation? To be clear what per file transpilations means: literally transpiling that one file with no other inputs. This is what ClosureBundler does. It is not what whitespace only or hotswap does. At least I'm guessing with hotswap that when it goes to hotswap the inputs still exist from the first compile, so you should be able to find the other modules. At least I hope. It not each step can always cache that information from the first time it runs with full compile. My plan for true per file transpilation was to transform ES6 modules to CJS(like) modules so that a missing module is a runtime error. Obviously we cannot detect it correctly if you only give us a single file. And yeah to support mutable exports there at all we need Object.defineProperty. Basically per file transpilation shouldn't ever ever assume things. That's bad. |
Oh that was just an illustration of the two different places we would need to recognize and rewrite based off then imported module type. No I usually wouldn't mix the same import like that in the same file - but it definitely could happen (and works today).
When I commonJS module imports another commonJS module, that path I listed is correct. But when a CommonJS module imports an ES6, there should be an import property listed.
I don't think that's good enough if you don't want to assume things. For per-file transpilation across modules, it requires a runtime check of the module type. What property an import references is dependent on both the host and target module types. And I don't use per file transpilation and would love it if that use case wasn't supported. But as long as it is I figured I'd raise the issues that have to be addressed. |
Right, and because you know what was an Es6 module input you can add the __esModule property to it. The same way babel and other CJS transformers do things. Anyways I think this may need a lot larger discussion here since I realizing how module rewriting is done in the compiler today is a huge blocker for this in general. Ordering sucks because every rewrite module step renames module scoped variables. So as it stands today you cannot actually move rewriting of references to module type into rewriter for module type because if it runs after some other module rewriter has run the local names are all already globalized.
Same here, at least with regards to ClosureBundler. Hotswap passes are not going away. But as it stands today CJS isn't compatible with ClosureBundler so it doesn't need to worry about it. It isn't compatible with ES6 modules either and I want to add support for them for internal people. Ideally it goes way but adding support is probably faster than killing it. But as it stands I think a lot of the code is making assumptions it doesn't need to. |
There is definitely a lot involved here with often competing concerns: module interop, type checking, dead code elimination, per-file transpilation, whole world optimization, etc. Whatever direction we go - we need a clear path charted that avoids one huge pr. The CommonJS pass is huge and complicated. |
Okay, so I think we've managed to find a quasi happy compromise for now. I've updated the document I wrote. The new tl;dr is:
Additionally we should be able to support |
Going to drop |
Expected result: 2 3. Actual result: 2 2.
Should be obvious just looking at this. All local variables are rewritten to globals, and exports are assigned to said globals at the bottom of the module. What needs to happen is if an export is mutable all references to it need to reference the export; no local.
The text was updated successfully, but these errors were encountered: