From 21244e7000dbbe8093dfd9e0c56fac9f97276c82 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sat, 20 Jun 2020 10:35:52 -0700 Subject: [PATCH] fix #190: ensure re-exported default aliases aren't renamed --- CHANGELOG.md | 6 +++ internal/bundler/bundler_test.go | 63 ++++++++++++++++++++++++++++++++ internal/parser/parser.go | 9 +++-- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9d56f0b775..711b5815f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +* Fix re-export statements ([#190](https://github.com/evanw/esbuild/issues/190)) + + The previous release caused a regression due to some behind-the-scenes work for the upcoming code splitting feature. The re-export alias in statements of the form `export { foo as bar } from 'path'` could sometimes incorrectly be renamed to something else, such as `foo` becoming `foo2`. This release fixes the bug. + ## 0.5.5 * Implement logical assignment operator transforms diff --git a/internal/bundler/bundler_test.go b/internal/bundler/bundler_test.go index bafeb1b0905..6387fbbef47 100644 --- a/internal/bundler/bundler_test.go +++ b/internal/bundler/bundler_test.go @@ -5310,3 +5310,66 @@ export { }, }) } + +func TestReExportDefault(t *testing.T) { + expectBundled(t, bundled{ + files: map[string]string{ + "/entry.js": ` + export {default as foo} from './foo' + export {default as bar} from './bar' + `, + "/foo.js": ` + export default 'foo' + `, + "/bar.js": ` + export default 'bar' + `, + }, + entryPaths: []string{"/entry.js"}, + parseOptions: parser.ParseOptions{ + IsBundling: true, + }, + bundleOptions: BundleOptions{ + IsBundling: true, + AbsOutputFile: "/out.js", + }, + expected: map[string]string{ + "/out.js": `// /foo.js +const foo_default = "foo"; + +// /bar.js +const bar_default = "bar"; + +// /entry.js +export { + bar_default as bar, + foo_default as foo +}; +`, + }, + }) +} + +func TestReExportDefaultNoBundle(t *testing.T) { + expectBundled(t, bundled{ + files: map[string]string{ + "/entry.js": ` + export {default as foo} from './foo' + export {default as bar} from './bar' + `, + }, + entryPaths: []string{"/entry.js"}, + parseOptions: parser.ParseOptions{ + IsBundling: false, + }, + bundleOptions: BundleOptions{ + IsBundling: false, + AbsOutputFile: "/out.js", + }, + expected: map[string]string{ + "/out.js": `export {default as foo} from "./foo"; +export {default as bar} from "./bar"; +`, + }, + }) +} diff --git a/internal/parser/parser.go b/internal/parser/parser.go index 87df4843dad..409e109c871 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -6965,7 +6965,7 @@ func (p *parser) visitAndAppendStmt(stmts []ast.Stmt, stmt ast.Stmt) []ast.Stmt } case *ast.SExportClause: - // "export {foo} + // "export {foo}" for i, item := range s.Items { name := p.loadNameFromRef(item.Name.Ref) ref := p.findSymbol(name).ref @@ -6980,10 +6980,13 @@ func (p *parser) visitAndAppendStmt(stmts []ast.Stmt, stmt ast.Stmt) []ast.Stmt p.currentScope.Generated = append(p.currentScope.Generated, s.NamespaceRef) p.recordDeclaredSymbol(s.NamespaceRef) - // This is a re-export and the names are symbols in another file + // This is a re-export and the symbols created here are used to reference + // names in another file. This means the symbols are really aliases. The + // symbols are marked as "unbound" so that they aren't accidentally renamed + // by the code that avoids symbol name collisions. for i, item := range s.Items { name := p.loadNameFromRef(item.Name.Ref) - ref := p.newSymbol(ast.SymbolOther, name) + ref := p.newSymbol(ast.SymbolUnbound, name) p.currentScope.Generated = append(p.currentScope.Generated, ref) p.recordDeclaredSymbol(ref) s.Items[i].Name.Ref = ref