feat: namespace → flat export migration (Bus proof-of-concept)#22685
Conversation
Convert `export namespace Bus { ... }` to flat named exports in bus.ts
with an `export * as Bus` barrel in index.ts. This lets bundlers
tree-shake individual Bus exports instead of keeping the entire
namespace as an opaque IIFE.
Also adds the unwrap-namespace automation script and migration spec.
|
Just fyi because I came across this PR - I was just looking into tree-shaking namespace exports, and it seems like the Bun bundler does not support tree-shaking Curious if you have any workarounds to this or if you're expecting opencode to switch to a different bundler, but for now this seems to be a limitation of Bun |
|
@jan-wilhelm Good catch — we verified this and you're right. Neither Bun's bundler nor esbuild can tree-shake We tested all three bundlers with a minimal repro:
Only Rollup handles it — it does AST-level property access tracking through namespace re-exports. The workaround: a Rollup tree-shaking pre-pass before Bun's compile step. Rollup resolves internal source and drops unused exports + their transitive deps, then Bun compiles the tree-shaken output into the binary. Draft PR: #22819 Generated by Claude Code |
Summary
export namespace Bus { ... }to flat named exports inbus.tswith anexport * as Busbarrel inindex.ts— proving the tree-shake migration pattern worksscript/unwrap-namespace.tsautomation script (uses ast-grep for accurate namespace boundary detection)specs/effect/namespace-treeshake.mdmigration plan and playbookWhy
TypeScript
export namespacecompiles to an IIFE that bundlers cannot tree-shake. The worst case:cli/error.tsimports{ Provider }just for 6.isInstance()checks, which forces inclusion of 20+ AI SDK packages. This inflates binary size, startup time, and memory.export * as Bus from "./bus"gives bundlers a static export list they can analyze — same consumer API (Bus.publish,Bus.Service, etc.), but unused exports and their transitive deps get dropped.What changed
Bus module (proof-of-concept):
src/bus/index.ts: 194 lines → 1 line (export * as Bus from "./bus")src/bus/bus.ts: new file with the unwrapped flat exportsindex.ts)Automation:
script/unwrap-namespace.ts: one-command conversion per module with--dry-runsupportspecs/effect/namespace-treeshake.md: full migration plan, tutorial, and playbookHow to convert the next module