Commit 6c7e972
authored
fix(bundler): barrel optimization drops exports used by dynamic import (#27695)
## What does this PR do?
Fixes invalid JS output when a `sideEffects: false` barrel is used by
both a **static named import** and a **dynamic `import()`** in the same
build with `--splitting --format=esm`.
## Root Cause
`src/bundler/barrel_imports.zig` had a heuristic that skipped escalating
`requested_exports` to `.all` for `import()` when the target already had
a partial entry from a static import:
```zig
} else if (ir.kind == .dynamic) {
// Only escalate to .all if no prior requests exist for this target.
if (!this.requested_exports.contains(target)) {
try this.requested_exports.put(this.allocator(), target, .all);
}
}
```
This is unsafe — `await import()` returns the **full module namespace**
at runtime. The consumer can destructure or access any export and we
can't statically determine which ones.
## Failure chain
1. Static `import { a } from "barrel"` seeds `requested_exports[barrel]
= .partial{"a"}`
2. Dynamic `import("barrel")` is ignored because
`requested_exports.contains(barrel)` is true
3. Barrel optimization marks `export { b } from "./b.js"` as `is_unused`
→ `source_index` cleared
4. Code splitting makes the barrel a chunk entry point (dynamic import →
own chunk)
5. Linker can't resolve the barrel's re-export symbol for `b` → uses the
unbound re-export ref directly
6. Output: `export { b2 as b }` where `b2` has no declaration →
**SyntaxError at runtime**
With `--bytecode --compile` this manifests as an `OutputFileListBuilder`
assertion (`total_insertions != output_files.items.len`) because JSC
rejects the chunk during bytecode generation, leaving an unfilled slot.
## Real-world trigger
`@smithy/credential-provider-imds` is a `sideEffects: false` barrel used
by:
- `@aws-sdk/credential-providers` — static: `import {
fromInstanceMetadata } from "@smithy/credential-provider-imds"`
- `@smithy/util-defaults-mode-node` — dynamic: `const {
getInstanceMetadataEndpoint, httpRequest } = await
import("@smithy/credential-provider-imds")`
## Fix
Dynamic import **always** marks the target as `.all` in both Phase 1
seeding and Phase 2 BFS. This is the same treatment as `require()`.
## Tests
- **New:** `barrel/DynamicImportWithStaticImportSameTarget` — repros the
bug with `--splitting`, verifies both exports work at runtime. Fails on
system bun with `SyntaxError: Exported binding 'b' needs to refer to a
top-level declared variable.`
- **Updated:** `barrel/DynamicImportInSubmodule` — was testing that a
fire-and-forget `import()` doesn't force unused submodules to load. That
optimization can't be safely applied, so the test now verifies the
conservative (correct) behavior: all barrel exports are preserved.1 parent 32edef7 commit 6c7e972
File tree
2 files changed
+100
-15
lines changed- src/bundler
- test/bundler
2 files changed
+100
-15
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
301 | 301 | | |
302 | 302 | | |
303 | 303 | | |
304 | | - | |
305 | | - | |
306 | | - | |
307 | | - | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
308 | 307 | | |
309 | 308 | | |
310 | 309 | | |
| |||
319 | 318 | | |
320 | 319 | | |
321 | 320 | | |
322 | | - | |
323 | | - | |
324 | | - | |
325 | | - | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
326 | 324 | | |
327 | 325 | | |
328 | 326 | | |
| |||
354 | 352 | | |
355 | 353 | | |
356 | 354 | | |
357 | | - | |
358 | | - | |
| 355 | + | |
| 356 | + | |
359 | 357 | | |
360 | 358 | | |
361 | 359 | | |
| |||
366 | 364 | | |
367 | 365 | | |
368 | 366 | | |
369 | | - | |
370 | | - | |
| 367 | + | |
371 | 368 | | |
372 | 369 | | |
373 | 370 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
541 | 541 | | |
542 | 542 | | |
543 | 543 | | |
544 | | - | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
545 | 547 | | |
546 | 548 | | |
547 | 549 | | |
| |||
562 | 564 | | |
563 | 565 | | |
564 | 566 | | |
565 | | - | |
566 | 567 | | |
567 | | - | |
| 568 | + | |
568 | 569 | | |
569 | 570 | | |
570 | 571 | | |
571 | 572 | | |
572 | 573 | | |
| 574 | + | |
| 575 | + | |
573 | 576 | | |
574 | 577 | | |
575 | 578 | | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
576 | 664 | | |
577 | 665 | | |
578 | 666 | | |
| |||
0 commit comments