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
Feat: add syntheticNamedExports #3295
Feat: add syntheticNamedExports #3295
Conversation
634a34b
to
150abcf
Compare
Codecov Report
@@ Coverage Diff @@
## master #3295 +/- ##
==========================================
+ Coverage 93.12% 93.16% +0.03%
==========================================
Files 170 171 +1
Lines 5966 6013 +47
Branches 1781 1794 +13
==========================================
+ Hits 5556 5602 +46
Misses 219 219
- Partials 191 192 +1
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.
Just a first look. I added a longer comment, the thing is I would prefer if we could change this feature to pick the properties off the namespace object on access instead to ensure live-binding and circular import support.
test/chunking-form/samples/synthetic-named-exports/_expected/es/main.js
Outdated
Show resolved
Hide resolved
test/chunking-form/samples/mixed-synthetic-named-exports/_expected/es/main.js
Show resolved
Hide resolved
be964b5
to
004fb25
Compare
test/chunking-form/samples/synthetic-named-exports/_expected/system/main.js
Outdated
Show resolved
Hide resolved
5d3c36c
to
0bf2edb
Compare
I had to add new code to handle this case: import * as ns from './dep1.js';
console.log(ns['some-prop']); to generate: const d = {};
console.log(d["some-prop"]); |
@manucorporat since this has been moved out of draft and ready for review, could you please replace "WIP" with a solid description of the PR and what it adds? Lukas knows what's up but for the rest of the folks not familiar what synthetic named exports are. |
@shellscape absolutely, thanks for the reminder, working on it! |
@lukastaegert @shellscape updated the description! make let me know if it's clear enough, or it needs more explanation |
@manucorporat superb, thank you. 👍 for Should we create a small core plugin that utilizes this? Would that reduce adoption friction? |
I think we could give it a try in commonjs-plugin first because it would solve a lot of problem there. We (at stencil) are also working in a much better web assembly plugin that would use this API and emitFile(), so we can load wasm using the most performant But not sure, yet if this feature should be applied to all the modules as a global setting (or a core plugin that enabled it by default) |
Excellent. Not to get too far on a tangent, but please do consider putting that work toward @rollup/plugin-wasm rather than a new plugin. We don't currently have a champion for that core plugin and would welcome it. |
@shellscape of course, we are happy to contribute it to the community but for the sake of iterating fast we are experimenting locally! import { method } from './a.wasm'; part of the design is to make it easy to integrate with language-specific plugins, like a #[no_mangle]
pub extern fn foo(a: i32, b: i32) -> i32 {
a + b
} main.js: import { foo } from './rust.rs';
foo(1, 2) // 3 |
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.
Looks really good. Just some minor suggestions. The last thing we need before release is to add documentation to the plugins
section in the docs, but I could also help you with that once I am through the other reviews.
src/Module.ts
Outdated
let syntheticExport = this.syntheticExports.get(name); | ||
if (!syntheticExport && !this.exports[name]) { | ||
const defaultExport = this.getDefaultExport(); | ||
if (defaultExport) { |
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 check should be superfluous as this.getDefaultExport
throws when it cannot find an export. Therefore, the check if (syntheticExport)
below is also unneeded and this can be simplified. Furthermore, the test for !this.exports[name]
should always be true as otherwise, we would not get here.
@lukastaegert all done! |
src/Module.ts
Outdated
if (syntheticExport) { | ||
return syntheticExport; | ||
} | ||
if (!this.exports[name]) { |
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.
Still wondering about this check. Shouldn’t L450-L456 make sure that this condition is always fulfilled because otherwise we would have done an early return there?
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.
ups! done
@@ -0,0 +1,25 @@ | |||
import Module, { AstContext } from '../../Module'; |
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.
One minor thing: Maybe call this a ...Variable so that it fits the names of the other variable types
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.
done!
Do you want to add that in this PR? or a different one? |
Documentation should ideally be part of the PR they document to avoid dependencies between PRs and make it easier to associate the two |
going to work on the docs today |
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.
Awesome!
Sorry for a bit of a necroposting, but as far as I can tell, this feature doesn't help with the case of modules that have both named and default exports, e.g. in terms of CommonJS, something like module.exports = {
default: 'foo',
helper: 'bar'
} (this is basically what Babel did when converting ES6 exports to ES5, at least at some point) Am I missing something? EDIT: Sorry, I'm being an idiot, please disregard. I'll blame sleep deprivation. |
The assumption is that the default export is never a key named |
This PR contains:
Are tests included?
Breaking Changes?
List any relevant issue numbers:
Description
Today named exports have to be known at compiler time in order to be imported within a different module:
foo
,bar
,thing
andstuff
have to be known exports at compiler time somod.js
should look like this:However, there are cases where this is not always possible or optimal:
The commonjs plugins performs a best-effort static analysis on top of commonjs code, in order to known at compiler time the "named" export of a cjs module. But this is not always possible, commonjs is highly dynamic and determined at runtime: circular dependencies or even creative ways of exporting things (https://github.com/npm/node-semver/blob/master/index.js#L7-L64) make it very difficult or impossible (requiring the plugin to have a
namedExports
option to workaround this limitation).Web assembly exported functions can not be infered at compiler time without MBs of native code making very hard to implement in rollup named exports of WASM modules:
Same could be applied to languages like Rust:
This is big deal with we want rollup to follow the future standard way of importing WASM:
https://github.com/WebAssembly/esm-integration/tree/master/proposals/esm-integration
Proposed solution
This PR adds a new plugin-only option called
syntheticNamedExports
(name up to debate), that just likemoduleSideEffects
can be returned by theresolveId
,load
andtransform
hooks.syntheticNamedExports
is a boolean that allows rollup to fallback the resolution named exports to properties of the default export, example:dep.js:
main.js
Plugin
Generated code:
Notice that even though
dep.js
does not exportfoo
,bar
, thanks to the new option, rollup is able to fallback the missing export to the property access of the default export.Design notes
syntheticNamedExports
isfalse
by default, preventing any kind of breaking change