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: handle re-exports of synthetic named exports #3319

Merged
merged 14 commits into from Apr 7, 2020

Conversation

manucorporat
Copy link
Contributor

@manucorporat manucorporat commented Jan 5, 2020

This PR contains:

  • bugfix
  • feature
  • refactor
  • documentation
  • other

Are tests included?

  • yes (bugfixes and features will not be merged without tests)
  • no

Breaking Changes?

  • yes (breaking changes will not be merged unless absolutely necessary)
  • no

List any relevant issue numbers:

Description

Fixes issues with synthetic named exports when using them cross-chunk and (re-)exporting them. TODO:

  • Re-exported synthetic named exports need to be included in generated namespace objects
  • Check handling of external files

@manucorporat
Copy link
Contributor Author

manucorporat commented Jan 5, 2020

@lukastaegert systemjs finalizer might be a little trickier, any tip?

@manucorporat manucorporat marked this pull request as ready for review January 5, 2020 16:45
@codecov
Copy link

codecov bot commented Jan 5, 2020

Codecov Report

Merging #3319 into master will increase coverage by 0.02%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3319      +/-   ##
==========================================
+ Coverage   95.92%   95.94%   +0.02%     
==========================================
  Files         174      174              
  Lines        5911     5950      +39     
  Branches     1739     1752      +13     
==========================================
+ Hits         5670     5709      +39     
  Misses        124      124              
  Partials      117      117              
Impacted Files Coverage Δ
src/Chunk.ts 99.78% <100.00%> (+<0.01%) ⬆️
src/Module.ts 98.87% <100.00%> (+0.02%) ⬆️
src/ast/variables/NamespaceVariable.ts 100.00% <100.00%> (ø)
src/ast/variables/SyntheticNamedExportVariable.ts 92.30% <100.00%> (+4.80%) ⬆️
src/ast/variables/Variable.ts 95.83% <100.00%> (-0.33%) ⬇️
src/finalisers/es.ts 100.00% <100.00%> (ø)
src/finalisers/system.ts 96.62% <100.00%> (+0.15%) ⬆️
src/utils/deconflictChunk.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d18cb37...1b81c71. Read the comment docs.

@manucorporat
Copy link
Contributor Author

Just noticed it's not working great with multiple entry-points, working in a fix

Copy link
Member

@lukastaegert lukastaegert left a comment

Choose a reason for hiding this comment

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

I see this is still WIP, still some comments, unfortunately probably not too helpful ones, including a broken test case. The question of variable name conflicts is also a tough one.

src/Chunk.ts Outdated
@@ -63,6 +64,7 @@ export interface ModuleDeclarationDependency {
export type ChunkDependencies = ModuleDeclarationDependency[];

export type ChunkExports = {
auxLocal: string | null;
Copy link
Member

Choose a reason for hiding this comment

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

What does auxLocal stand for? Can we find a better name so that it is clearer what it does?

getName(): string {
const name = this.name;
const renderBaseName = this.defaultVariable.getName();
console.log(this.defaultVariable.getOriginalVariable());
Copy link
Member

Choose a reason for hiding this comment

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

log statement

src/Chunk.ts Outdated Show resolved Hide resolved
} else {
if (specifier.auxLocal) {
exportBlock.push(`${varOrConst} ${specifier.auxLocal}${_}=${_}${local};`);
local = specifier.auxLocal;
Copy link
Member

Choose a reason for hiding this comment

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

Is there any logic that makes sure that this new variable does not conflict with an existing one? Worst case, it could conflict with an accessed global variable, in which case the used specifier CANNOT be identical with the property name. In all other cases, it would still need to be deconflicted with all imports and other top-level variables. Unfortunately I do not see a trivial solution for this yet.

@@ -0,0 +1,3 @@
var dep2 = {bar: {foo: 'works'}};

export default dep2;
Copy link
Member

Choose a reason for hiding this comment

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

I do not think this should be rendered as a default export. Usually, there are no default exports in auto-generated chunks and everything is a named export. Furthermore, the named exports are also minified. I think we definitely need this here as well, the most important reason being that a file can have only one default export while a chunk could potentially have many of those synthetic exports belonging to different default exports. I think this bug is also shown in the test case I added in the other comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

you right! I just noticed rollup never emits default exports in generated chunks

src/Chunk.ts Outdated
exports.push({
auxLocal,
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it is a property? I really find auxLocal unhelpful as it does not tell me what this value means. property could tell me that this is the property we are exporting. The fact that I need to introduce a local variable is of course another question.

@manucorporat
Copy link
Contributor Author

@lukastaegert applied some of your feedback, still a couple of things pending.
But added some code to prevent conflict, not sure if it's correct though. seems like it's a little hacky where i added it

src/Chunk.ts Outdated
@@ -1131,11 +1157,13 @@ export default class Chunk {
exportVariable.setRenderNames('exports', exportName);
} else {
exportVariable.setRenderNames(null, null);
if (options.format === 'es' && exportVariable instanceof SyntheticNamedExportVariable) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

needs a comment explaining why:

// ESM exports of synthetic events will render a new variable, here we make sure it's deconflicted

src/Chunk.ts Outdated
variable instanceof ExportDefaultVariable ? variable.getOriginalVariable() : variable;
variable instanceof ExportDefaultVariable ||
variable instanceof SyntheticNamedExportVariable
? variable.getOriginalVariable()
Copy link
Member

Choose a reason for hiding this comment

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

At the moment, this will only go one level deep. It would be nice if when a default export is itself a synthetic named export, only the original default export is ever passed between chunks. I created a broken test case for this that currently produces wild results (synthetic-named-exports-multi-level)

src/Chunk.ts Outdated
: variable.module!.chunk!.getVariableExportName(variable),
local: variable.getName()
});
if (variable instanceof SyntheticNamedExportVariable) {
Copy link
Member

Choose a reason for hiding this comment

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

So what we did together with the code in line 964 is that we replaced variable with variable.defaultVariable if it was a synthetic named export and now we basically do it a second time if the default itself was synthetic. What it should do, however, is work for arbitrary levels of nesting, see one of the test cases I added.

src/Chunk.ts Outdated Show resolved Hide resolved
src/Chunk.ts Outdated Show resolved Hide resolved
src/finalisers/esm.ts Outdated Show resolved Hide resolved
@lukastaegert
Copy link
Member

Hi, I have some time tomorrow where I could work on this if you want, but just an offer as it is your PR.

@LarsDenBakker
Copy link
Contributor

@manucorporat anything that can be done to help move this along? :)

@manucorporat
Copy link
Contributor Author

Unfortunately, it's being hard to find time to keep working in this PR from a mental and working perspective, feel free anyone to keep working on it.

Otherwise since this is not high priority i will try to find sometime this summer!

Sorry

@shellscape
Copy link
Contributor

@lukastaegert @LarsDenBakker please do attack this

@manucorporat no worries, we understand!

@lukastaegert
Copy link
Member

I will try to tackle this one now.

@lukastaegert lukastaegert self-assigned this Apr 5, 2020
@lukastaegert
Copy link
Member

Actually had some progress here, reexports are now working in most cases. Remaining things to check and/or fix that I am aware of:

  • Generated namespace objects omit re-exported synthetic named exports at the moment
  • Handling of external files with synthetic named exports should be investigated

Will try to fix them in the next days.

@lukastaegert
Copy link
Member

From my side, this is now ready for release! Quite a lot of stuff has been fixed. I will have a last look through the code and push a patch release so that the CJS plugin can move forward.

@lukastaegert lukastaegert merged commit aecd3e1 into rollup:master Apr 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants