-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Rework interop handling #3710
Rework interop handling #3710
Conversation
Thank you for your contribution! ❤️You can try out this pull request locally by installing Rollup via
or load it into the REPL: |
Codecov Report
@@ Coverage Diff @@
## master #3710 +/- ##
==========================================
+ Coverage 96.80% 96.96% +0.16%
==========================================
Files 183 184 +1
Lines 6320 6402 +82
Branches 1843 1854 +11
==========================================
+ Hits 6118 6208 +90
+ Misses 105 103 -2
+ Partials 97 91 -6
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do ping when you've got the docs up.
@@ -2,7 +2,7 @@ define(['exports', 'external'], function (exports, external) { 'use strict'; | |||
|
|||
|
|||
|
|||
exports.p = external.default; | |||
exports.p = external; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a bug fix!?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a matter of fact yes, here, reexports behaved differently from regular imports where the .default
was not added.
@guybedford I added the documentation and also pasted it into the PR description. By tomorrow, I will try to compile what other changes are included as some hidden bugs were fixed as well. |
I also compiled the changelog now, this is a list of the included fixes and improvements: Features
Bug Fixes
|
e651d11
to
2c02522
Compare
This is now complete and ready for review |
very impressive list of changes! Definitely looking forward to this. The description mentions that dynamic imports are impacted too, but doesn't give any examples of the codegen changes for those. could you add more details around that? I expect it'll be the same as the |
Yes, exactly. Maybe I extend the examples to also contain a dynamic import as it would of course use the same helper. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is some really great work and a solid model!
One extension of auto in future that I always imagined would be interesting for __esModule
would be to check ns[Symbol.toStringTag] === 'Module'
as equivalent to the __esModule
branding check. This way it could be possible to have a autoModern
or something mode which does "the right thing" for externals in both Node.js and browsers without needing the per-module function to support that.
Also, the modern module brand check can be done with |
f67663a
to
b06eb19
Compare
@guybedford That is definitely an interesting idea. If I understand correctly, though, it would involve Rollup's cjs/amd/iife/umd output to replace or augment Object.defineProperty(exports, '__esModule', { value: true }); with exports[Symbol.toStringTag] = 'Module'; or alternatively Object.defineProperty(exports, 'toString', { value: function() { return '[object Module]'; } }); for a legacy version to work correctly? @dgoldstein0 I extended the examples to also contain a dynamic import as well, I hope this answers your question. |
@lukastaegert actually thinking about this more I'm not sure it solves anything... sorry for false suggestion. I don't think we should ever change the __esModule brand because that is a reliable differentiator. Rather this was about environment inference, but in an ES module environment we just want to be treating everything as a namespace anyway so perhaps best not to meddle. |
yes it does, thanks a ton! |
…freeze interop namespaces, mark interop helpers as pure
…rn when reexporting the namespace
b06eb19
to
188ad34
Compare
includes workaround for issues introduced in rollup/rollup#3710
This PR contains:
Are tests included?
Breaking Changes?
List any relevant issue numbers:
Resolves #3288
Resolves #2554
Description
This completely reimplements how interop is handled. I.e. in formats cjs, amd, iife and umd, this reimplements how default and namespace imports and reexports are handled, as well as what is returned by dynamic imports from external dependencies.
Besides the existing values, there are now four new interop values: "auto", "esModule", "default" and "defaultOnly". Moreover, a function can be supplied for
output.interop
to configure the correct interop per dependency.From the new documentation:
output.interop
Type:
"auto" | "esModule" | "default" | "defaultOnly" | boolean | ((id: string) => "auto" | "esModule" | "default" | "defaultOnly" | boolean)
CLI:
--interop <value>
Default:
true
Controls how Rollup handles default, namespace and dynamic imports from external dependencies in formats like CommonJS that do not natively support these concepts. Note that even though
true
is the current default value, this value is deprecated and will be replaced by"auto"
in the next major version of Rollup. In the examples, we will be using the CommonJS format, but the interop similarly applies to AMD, IIFE and UMD targets as well.To understand the different values, assume we are bundling the following code for a
cjs
target:Keep in mind that for Rollup,
import * as ext_namespace from 'external'; console.log(ext_namespace.bar);
is completely equivalent toimport {bar} from 'external'; console.log(bar);
and will produce the same code. In the example above however, the namespace object itself is passed to a global function as well, which means we need it as a properly formed object."esModule"
assumes that required modules are transpiled ES modules where the required value corresponds to the module namespace and the default export is the.default
property of the exported object:When
esModule
is used, Rollup adds no additional interop helpers and also supports live-bindings for default exports."default"
assumes that the required value should be treated as the default export of the imported module, just like when importing CommonJS from an ES module context in Node. In contrast to Node, though, named imports are supported as well which are treated as properties of the default import. To create the namespace object, Rollup injects helpers:"auto"
combines both"esModule"
and"default"
by injecting helpers that contain code that detects at runtime if the required value contains the__esModule
property. Adding this property is a standard implemented by Rollup, Babel and many other tools to signify that the required value is the namespace of a transpiled ES module:Note how Rollup is reusing the created namespace object to get the
default
export. If the namespace object is not needed, Rollup will use a simpler helper:"defaultOnly"
is similar to"default"
except for the following:es
andsystem
formats. That way it is ensure that thees
version of the code is able to import non-builtin CommonJS modules in Node correctly.export * from 'external';
are not prohibited, they are ignored and will cause Rollup to display a warning because they would not have an effect if there are no named exports.Here is what Rollup will create from the example code. Note that we removed
external.bar
from the code as otherwise, Rollup would have thrown an error because, as stated before, this is equivalent to a named import.When a function is supplied, Rollup will pass each external id to this function once to control the interop type per dependency.
As an example if all dependencies are CommonJs, the following config will ensure that named imports are only permitted from Node builtins:
true
is equivalent to"auto"
except that it uses a slightly different helper for the default export that checks for the presence of adefault
property instead of the__esModule
property.☢️ This value is deprecated and will be removed in a future Rollup version.
false
is equivalent to usingdefault
when importing a default export andesModule
when importing a namespace.☢️ This value is deprecated and will be removed in a future Rollup version.
There are some additional options that have an effect on the generated interop code:
output.exernalLiveBindings
tofalse
will generate simplified namespace helpers as well as simplified code for extracted default imports.output.freeze
tofalse
will prevent generated interop namespace objects from being frozen.Included fixes and improvements
Features
require
statements to the top in CommonJS output for easier back-transpilation to ES modules by other tools (Rework interop handling #3710)Bug Fixes