fix: CLIPlugin dynamic import interop under Bun#4799
Conversation
Bun honors the __esModule marker when dynamic-importing the compiled CJS, so the Node-interop double .default unwrap yields undefined and every build crashes with 'undefined is not a constructor'.
🦋 Changeset detectedLatest commit: 5727a55 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4799 +/- ##
=======================================
Coverage 93.30% 93.30%
=======================================
Files 14 14
Lines 5463 5466 +3
Branches 792 793 +1
=======================================
+ Hits 5097 5100 +3
Misses 364 364
Partials 2 2
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Fixes webpack-cli failing under Bun due to differing ESM↔CJS default-export interop behavior when lazily importing CLIPlugin.
Changes:
- Adjust
CLIPlugindynamic import to unwrap.defaultin an interop-safe way across Node and Bun. - Add a changeset to publish the fix as a patch release.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/webpack-cli/src/webpack-cli.ts | Updates lazy import logic to handle Node/Bun CJS interop differences when resolving CLIPlugin. |
| .changeset/cli-plugin-import-interop.md | Adds a patch changeset entry documenting the Bun interop fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Node's CJS interop nests the class under `.default.default`; Bun honors | ||
| // `__esModule` and unwraps once already, leaving the class one level up. | ||
| const cliPluginModule = (await import("./plugins/cli-plugin.js")).default; | ||
| const CLIPlugin = cliPluginModule.default ?? cliPluginModule; |
There was a problem hiding this comment.
Added in 5727a55: test/api/cli-plugin-interop/ mocks the cli-plugin module to the shape Bun's __esModule-aware interop produces (the class itself, no nested .default) and asserts a build still applies CLIPlugin. Verified it fails with "CLIPlugin is not a constructor" against the previous double-unwrap code.
Generated by Claude Code
Mocks the cli-plugin module to the shape Bun's __esModule-aware interop produces (the class itself) and asserts a build still applies CLIPlugin.
Summary
The CLIPlugin lazy import unwraps
.defaulttwice, which is correct for Node's CJS interop butundefinedunder Bun (it honors the__esModulemarker and unwraps once already), so everybun --bun webpack buildfails with "undefined is not a constructor"; unwrap interop-safely like the existingparserModulepattern.What kind of change does this PR introduce?
fix
Did you add tests for your changes?
No; covered by webpack/webpack#21334, which runs webpack's
WebpackCli.longtest.jssuite under Bun (existing suites here still pass).Does this PR introduce a breaking change?
No
If relevant, what needs to be documented once your changes are merged or what have you already documented?
n/a
Use of AI
Written with Claude Code: it diagnosed the Bun interop failure and authored the fix and changeset; verified via
tsc --build, a Bun/Node runtime repro, and webpack's WebpackCli suite under Bun.Generated by Claude Code