mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Fix duplicate exports when entry points re-export from other entry points (#5344)
When code splitting is enabled and one entry point re-exports from another entry point, the bundler was generating duplicate export statements. This happened because both the cross-chunk export generation logic and the entry point export generation logic were creating export statements for the same symbols. The fix prevents cross-chunk export statement generation for entry points that have their own sorted export aliases, since these entry points will generate their own exports in generateEntryPointTailJS(). Test cases added to verify: - Basic re-export scenario between two entry points - Multiple re-exports through intermediate modules 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -334,7 +334,13 @@ fn computeCrossChunkDependenciesWithChunkMetas(c: *LinkerContext, chunks: []Chun
|
||||
);
|
||||
}
|
||||
|
||||
if (clause_items.len > 0) {
|
||||
// Only generate cross-chunk export statements if there are items to export
|
||||
// and if this chunk is not an entry point with sorted export aliases
|
||||
// (which indicates it will generate its own exports in generateEntryPointTailJS)
|
||||
const sorted_and_filtered_export_aliases = c.graph.meta.items(.sorted_and_filtered_export_aliases)[chunk.entry_point.source_index];
|
||||
const should_skip_cross_chunk_exports = chunk.entry_point.is_entry_point and sorted_and_filtered_export_aliases.len > 0;
|
||||
|
||||
if (clause_items.len > 0 and !should_skip_cross_chunk_exports) {
|
||||
var stmts = BabyList(js_ast.Stmt).initCapacity(c.allocator, 1) catch unreachable;
|
||||
const export_clause = c.allocator.create(js_ast.S.ExportClause) catch unreachable;
|
||||
export_clause.* = .{
|
||||
|
||||
@@ -294,4 +294,65 @@ describe("bundler", () => {
|
||||
run: true,
|
||||
capture: ["1 /* Value */", "1 /* Value */", "1 /* Value */"],
|
||||
});
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/5344
|
||||
itBundled("regression/DuplicateExports#5344", {
|
||||
files: {
|
||||
"/entry-b.ts": `
|
||||
export function b() {}
|
||||
`,
|
||||
"/entry-a.ts": `
|
||||
export { b } from "./entry-b.ts";
|
||||
export function a() {}
|
||||
`,
|
||||
},
|
||||
entryPoints: ["/entry-a.ts", "/entry-b.ts"],
|
||||
outdir: "/out",
|
||||
format: "esm",
|
||||
splitting: true,
|
||||
onAfterBundle(api) {
|
||||
// Check entry-b.js output for duplicate exports
|
||||
const entryB = api.readFile("/out/entry-b.js");
|
||||
|
||||
// Count all export statements (both single line and multiline)
|
||||
const exportMatches = entryB.match(/export\s*\{[^}]*\}/g);
|
||||
|
||||
if (exportMatches && exportMatches.length > 1) {
|
||||
throw new Error(`Found ${exportMatches.length} export statements (expected 1) in entry-b.js:\n${exportMatches.join('\n')}\n\nFull output:\n${entryB}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Additional test case for multiple re-exports
|
||||
itBundled("regression/DuplicateExports#5344-MultipleReExports", {
|
||||
files: {
|
||||
"/lib.ts": `
|
||||
export const value = 42;
|
||||
export function helper() { return "helper"; }
|
||||
`,
|
||||
"/intermediate.ts": `
|
||||
export { value, helper } from "./lib.ts";
|
||||
`,
|
||||
"/entry.ts": `
|
||||
export { value, helper } from "./intermediate.ts";
|
||||
export const main = "main";
|
||||
`,
|
||||
},
|
||||
entryPoints: ["/entry.ts", "/intermediate.ts", "/lib.ts"],
|
||||
outdir: "/out",
|
||||
format: "esm",
|
||||
splitting: true,
|
||||
onAfterBundle(api) {
|
||||
// Check each file for duplicate exports
|
||||
const files = ["entry.js", "intermediate.js", "lib.js"];
|
||||
for (const file of files) {
|
||||
const content = api.readFile(`/out/${file}`);
|
||||
const exportMatches = content.match(/export\s*\{[^}]*\}/g);
|
||||
|
||||
if (exportMatches && exportMatches.length > 1) {
|
||||
throw new Error(`Found ${exportMatches.length} export statements (expected max 1) in ${file}:\n${exportMatches.join('\n')}\n\nFull output:\n${content}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user