Skip to content

build(core): ship per-module dist so optimizePackageImports works (bunchee → tsdown) - #216

Merged
childrentime merged 1 commit into
mainfrom
build/unbundled-dist-optimize-package-imports
Aug 5, 2026
Merged

build(core): ship per-module dist so optimizePackageImports works (bunchee → tsdown)#216
childrentime merged 1 commit into
mainfrom
build/unbundled-dist-optimize-package-imports

Conversation

@childrentime

Copy link
Copy Markdown
Owner

What

Replace bunchee with tsdown (unbundle: true, i.e. preserveModules) for @reactuses/core, so dist ships one file per module with the entry as a thin barrel of re-exports instead of a single 172 kB bundle:

  • tsdown.config.ts: esm + cjs, dts, unbundle, es2015 target
  • package.json exports: require now maps to ./dist/index.js / ./dist/index.d.ts (tsdown's CJS naming); ./useQRCode moves to ./dist/useQRCode/index.*; new ./* wildcard enables direct subpath imports (@reactuses/core/useDebounce) for any hook
  • root package.json: drop the now-unused bunchee dependency

Why

Barrel-file optimizers (Next.js optimizePackageImports, and bundler dev servers generally) can only "unroll" an import when the entry file re-exports from real per-module files. Our previous dist inlined all 120+ hooks into one bundle, so a Next.js (Turbopack) dev page importing just useDebounce pulled in every hook: a 552 kB client chunk. bunchee cannot emit unbundled output (and OOMs when given 112 entry points).

With this change the same page loads only useDebounce and its real dependency chain: 64 kB (−88%).

Test plan

  • pnpm --filter @reactuses/core test — 61 suites / 318 tests pass
  • pnpm --filter @reactuses/core run typecheck + pnpm lint pass
  • Packed the tarball into a Next.js 16.3 app:
    • dev with optimizePackageImports: ['@reactuses/core']: unused hooks no longer loaded (552 kB → 64 kB chunk)
    • next build (prod) passes
    • CJS require / ESM import, main entry + subpath, both resolve
    • tsc --noEmit under both bundler and nodenext resolution passes
    • useQRCode still externalizes the optional qrcode peer dep

🤖 Generated with Claude Code

…nchee → tsdown)

Barrel-file optimizers (Next.js optimizePackageImports, bundler dev
servers) can only unroll an import when the entry re-exports from real
per-module files. The previous dist inlined all 120+ hooks into one
bundle, so a Next.js dev page importing just useDebounce pulled in every
hook (552 kB client chunk). bunchee cannot emit unbundled output and
OOMs when given per-hook entry points.

Switch the core build to tsdown with unbundle (preserveModules): dist
now ships one file per module with a thin barrel entry. The same page
now loads only useDebounce and its dependency chain (64 kB, −88%).

- exports: require now maps to ./dist/index.js / ./dist/index.d.ts
  (tsdown CJS naming); ./useQRCode moves to ./dist/useQRCode/index.*;
  new "./*" wildcard enables direct subpath imports for any hook
- root package.json: drop the now-unused bunchee dependency

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@childrentime
childrentime merged commit 2d19563 into main Aug 5, 2026
4 checks passed
@childrentime
childrentime deleted the build/unbundled-dist-optimize-package-imports branch August 5, 2026 11:58
childrentime added a commit that referenced this pull request Aug 6, 2026
Why index.ts re-exports hurt tree shaking, Next.js dev memory, and tsc,
and how they breed circular dependencies — built on the real #216
bunchee→tsdown per-module dist migration (552 kB → 64 kB).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant