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

Default and export naming fixes #2001

Merged
merged 3 commits into from
Feb 25, 2018
Merged

Conversation

guybedford
Copy link
Contributor

This fixes #1996 along with some other cases around export default and named exports:

  • The issue was the fact that default exports get a temporary reassignment tracking variable. So the fix was to ensure this variable supports safeName and doesn't get a safe name when there's an original reassignment.
  • There was an issue with the CJS and AMD export mode on chunks being set to auto. Internal chunks need to always use named exports for consistency with the interfaces, which fixed a missed test case as well.
  • ensureExport now takes an export name argument. Export names can in theory be arbitrary, but when they match up with the exact signature of a chunk we allow chunks to become entry point chunks. So ensuring export name consistency helps ensure we don't create unnecessary facades.
  • Removed an unused code path in ensureExport
  • Default exports are now called default by default, while we were using their import aliases before.

The depths of these cases keeps expanding... I'm sure it will continue to! But as long as we put equal effort into refactoring and fixes we should be on a good footing (and project sponsorship has been a huge help to justify this time input!!).

@anandthakker
Copy link
Contributor

anandthakker commented Feb 21, 2018

@guybedford Tried this out over in mapbox/mapbox-gl-js#6196 and I'm seeing:

exports.* = commonjsGlobal;
exports.*$1 = unwrapExports;
exports.*$2 = createCommonjsModule;

in chunk1.js

@@ -14,6 +14,7 @@ export default class ExportDefaultVariable extends LocalVariable {
super(name, exportDefaultDeclaration, exportDefaultDeclaration.declaration);
this.isDefault = true;
this.hasId = !!(<FunctionDeclaration | ClassDeclaration>exportDefaultDeclaration.declaration).id;
this.exportName = 'default';
Copy link
Contributor

Choose a reason for hiding this comment

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

How hard would it be to include the filename of the module as part of this name, e.g., my_module$default for the default export of my-module.js? Seems like this could make for easier debugging in some cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason we use the initial name is because this stage of the algorithm is still attempting to determine if the chunk is an entry point module itself (entry.js), or if it is a chunk (chunk[i].js). Ideally the algorithm could do the export naming after this stage in the process in which case we can better handle naming definitely. I will look into a refactor along these lines, but perhaps not just yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(So basically in the scenario that the chunk is an entry point, we need it to do the correct naming as the entry point would expect)

@anandthakker
Copy link
Contributor

diff --git a/src/Chunk.ts b/src/Chunk.ts
index 6c01905c..69b6eea4 100644
--- a/src/Chunk.ts
+++ b/src/Chunk.ts
@@ -146,9 +146,10 @@ export default class Chunk {
                }

                let i = 0;
-               safeExportName = exportName;
+               const baseExportName = exportName === '*' ? '_namespace' : exportName;
+               safeExportName = baseExportName;
                while (this.exports[safeExportName]) {
-                       safeExportName = exportName + '$' + ++i;
+                       safeExportName = baseExportName + '$' + ++i;
                }
                variable.exportName = safeExportName;

Fixes the above for me

@guybedford
Copy link
Contributor Author

@anandthakker any chance of a test case? I take it you have a namespace import across a chunk boundary?

@anandthakker
Copy link
Contributor

@guybedford y, I'll try to reduce a test case. We don't have an explicit namespace import -- from the variable names (commonjsGlobal, unwrapExports, createCommonjsModule), my guess is that this is coming from a module produced by https://github.com/rollup/rollup-plugin-commonjs

@guybedford
Copy link
Contributor Author

Sure no stress, I'll run it on my test case here for that too and see what I can find.

@anandthakker
Copy link
Contributor

@guybedford
Copy link
Contributor Author

@anandthakker amazing thanks for isolating - my CJS case here didn't catch it.

@guybedford
Copy link
Contributor Author

@anandthakker I've included the fix here. Thanks again for the test cases.

@anandthakker
Copy link
Contributor

@guybedford thanks! Confirmed that it's working in practice over at https://github.com/mapbox/mapbox-gl-js/tree/6fa5becea62ac5a58a5eb1572fb764c64e50fa36

@anandthakker
Copy link
Contributor

(By the way, @guybedford, you can also see there the hacky way we're using code splitting to approximate the new Worker() strategy under discussion over at #1029)

@lukastaegert lukastaegert added this to the 0.56.3 milestone Feb 22, 2018
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.

Great work!

src/Chunk.ts Outdated
@@ -115,6 +115,8 @@ export default class Chunk {
this.dependencies = undefined;
this.entryModule = undefined;
this.isEntryModuleFacade = false;
if (orderedModules.length === 0)
this.isEntryModuleFacade = true;
Copy link
Member

Choose a reason for hiding this comment

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

This would easily merge with the previous assignment.

src/Chunk.ts Outdated
@@ -136,44 +138,30 @@ export default class Chunk {
// ensure that the module exports or reexports the given variable
// we don't replace reexports with the direct reexport from the final module
// as this might result in exposing an internal module which taints an entryModule chunk
ensureExport (module: Module | ExternalModule, variable: Variable): string {
ensureExport (module: Module | ExternalModule, variable: Variable, exportName: string): string {
// assert(module.chunk === this || module.isExternal);
let safeExportName = this.exportedVariables.get(variable);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should actually call this this.exportedVariableNames to make clear what we are mapping the variables to.

else {
curExport.name = safeExportName;
}

return safeExportName;
}
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -86,7 +86,7 @@ export default class Scope {
const declaration = this.variables[key];

// we can disregard exports.foo etc
if (declaration.exportName && declaration.isReassigned && !declaration.isId)
if (declaration.exportName && (declaration.isDefault || declaration.isReassigned && !declaration.isId))
Copy link
Member

Choose a reason for hiding this comment

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

The non-use of more parentheses here makes me a little uneasy (sorry, but I'm one of those people who need to look into an operator precedence table to see that && binds more strongly than ||).

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.

Awesome! I also very much like the additional private modifiers that will help us keep the API surface small.

@arackaf
Copy link

arackaf commented Feb 24, 2018

I'm trying to test this branch out, but I can't quite get npm to install it - can anyone here possibly lend a hand? As far as I can tell, this is what I want

npm i https://github.com/rollup/rollup.git#default-identifier-renaming --save

but npm doesn't seem to like it (nor plenty of the variations on that theme which I've also tried)

This seems to fail too :(

npm i rollup/rollup#default-identifier-renaming --save

@lukastaegert
Copy link
Member

Hi @arackaf,

you should probably check your npm version. As rollup relies on its prepare script being executed to build its bundle, direct Github installation will only work for npm versions 5.0.0 and later. As I just confirmed, npm i rollup/rollup#default-identifier-renaming --save should work with current npm versions.

Nevertheless, I am about to release this branch now anyway 😉

@lukastaegert lukastaegert merged commit db17e0f into master Feb 25, 2018
@lukastaegert lukastaegert deleted the default-identifier-renaming branch February 25, 2018 18:32
@arackaf
Copy link

arackaf commented Feb 25, 2018

Aha! Thanks Lukas. I'm still too scared to go back in the npm 5 waters. I reverted back to the comfort and safety of npm 4 since many of the 5 bugs affected me directly.

@guybedford
Copy link
Contributor Author

@arackaf for future reference try git://github.com/rollup/rollup#default-identifier-renaming.

@lukastaegert
Copy link
Member

@arackaf Might be worth giving npm@5 another shot. A lot has been fixed and improved since 5.0.0. We're using it at work for two large projects and after some initial hickups, it's now been smooth riding for quite a few months now. The performance benefits are nothing to sneeze at 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants