Discovered while debugging @stacksjs/bun-router@0.0.5's published types being effectively empty. Filing because the failure mode is silent and the recovery path isn't obvious.
Repro
build.ts:
import { dts } from 'bun-plugin-dtsx'
await Bun.build({
entrypoints: ['src/index.ts', 'src/cli.ts'],
outdir: './dist',
splitting: true,
target: 'bun',
format: 'esm',
plugins: [dts({ entrypoints: ['index.ts', 'cli.ts'] })],
})
src/index.ts (a typical barrel):
export * from './router'
export * from './middleware'
export * from './types'
export * from './utils'
// …
Run the build. Result in dist/:
dist/
index.d.ts ← contains: export * from './router'; export * from './types'; …
cli.d.ts
index.js
cli.js
chunk-*.js
No dist/router.d.ts, no dist/types.d.ts, no dist/router/, no dist/types/. The barrel re-exports point at sibling modules that were never emitted, because they weren't in the plugin's entrypoints array.
Effect
TypeScript silently treats every export * from './missing' as exporting nothing. Consumers see:
TS2305: Module '"@stacksjs/bun-router"' has no exported member 'EnhancedRequest'
TS2305: Module '"@stacksjs/bun-router"' has no exported member 'Router'
TS2305: Module '"@stacksjs/bun-router"' has no exported member 'ActionHandler'
…
Cascading further: types that do resolve get inferred as any (because their dependent types are missing), causing TS7034 (Variable 'route' implicitly has type 'any' in some locations…) at usage sites. The runtime JS is fine — everything's bundled into index.js. Just the types are broken.
This already shipped to npm: @stacksjs/bun-router@0.0.5. A fresh app installing it gets a router with no types.
bundle: true doesn't fix it
plugins: [dts({ entrypoints: ['index.ts', 'cli.ts'], bundle: true })]
Output is still:
// dist/index.d.ts
export * from './router'
export * from './types'
// …
bundle: true concatenates the entrypoint files into one output, but doesn't follow export * from './x' and inline what those files export. Other dts bundlers (rollup-plugin-dts, tsup, microsoft/api-extractor) follow re-exports and inline reachable types — that's what "bundle" usually means in this space.
Workarounds we tried
-
Pass every source file as an entrypoint: dts({ entrypoints: glob('src/**/*.ts') }). Works (mirrors src tree to dist), but slow with many files (~7 minutes for 131 files in our repro before we cancelled it).
-
Replace the dts plugin with tsc -p tsconfig.build.json: ran in parallel with Bun.build(). Fast, complete output, but defeats the purpose of using bun-plugin-dtsx.
We ended up with #2.
What I'd expect
At least one of:
bundle: true actually bundles — follows export * / export {x} from './path' chains, inlines reachable declarations into a single rolled-up index.d.ts. This is the most useful default and matches how every other dts bundler behaves.
- Auto-include reachable subpaths — when an entrypoint's
.d.ts re-exports from sibling modules, emit .d.ts for those siblings too. Recursively.
- Fail loudly — at minimum, emit a build warning/error when the generated
.d.ts references a sibling that isn't being emitted: "Generated dist/index.d.ts re-exports from ./router, but no entrypoint produces dist/router.d.ts. The output is unusable." This alone would have saved us hours.
Environment
bun-plugin-dtsx@0.9.13
@stacksjs/dtsx@0.9.13
- Bun 1.3.10
- macOS 14.x
Cross-reference
Real-world impact: published @stacksjs/bun-router@0.0.5 shipped with broken types. Companion bun-router proposal/cleanup will follow once this is sorted (or worked around).
Discovered while debugging
@stacksjs/bun-router@0.0.5's published types being effectively empty. Filing because the failure mode is silent and the recovery path isn't obvious.Repro
build.ts:src/index.ts(a typical barrel):Run the build. Result in
dist/:No
dist/router.d.ts, nodist/types.d.ts, nodist/router/, nodist/types/. The barrel re-exports point at sibling modules that were never emitted, because they weren't in the plugin'sentrypointsarray.Effect
TypeScript silently treats every
export * from './missing'as exporting nothing. Consumers see:Cascading further: types that do resolve get inferred as
any(because their dependent types are missing), causing TS7034 (Variable 'route' implicitly has type 'any' in some locations…) at usage sites. The runtime JS is fine — everything's bundled intoindex.js. Just the types are broken.This already shipped to npm:
@stacksjs/bun-router@0.0.5. A fresh app installing it gets a router with no types.bundle: truedoesn't fix itOutput is still:
bundle: trueconcatenates the entrypoint files into one output, but doesn't followexport * from './x'and inline what those files export. Other dts bundlers (rollup-plugin-dts, tsup, microsoft/api-extractor) follow re-exports and inline reachable types — that's what "bundle" usually means in this space.Workarounds we tried
Pass every source file as an entrypoint:
dts({ entrypoints: glob('src/**/*.ts') }). Works (mirrors src tree to dist), but slow with many files (~7 minutes for 131 files in our repro before we cancelled it).Replace the dts plugin with
tsc -p tsconfig.build.json: ran in parallel withBun.build(). Fast, complete output, but defeats the purpose of usingbun-plugin-dtsx.We ended up with #2.
What I'd expect
At least one of:
bundle: trueactually bundles — followsexport */export {x} from './path'chains, inlines reachable declarations into a single rolled-upindex.d.ts. This is the most useful default and matches how every other dts bundler behaves..d.tsre-exports from sibling modules, emit.d.tsfor those siblings too. Recursively..d.tsreferences a sibling that isn't being emitted: "Generateddist/index.d.tsre-exports from./router, but no entrypoint producesdist/router.d.ts. The output is unusable." This alone would have saved us hours.Environment
bun-plugin-dtsx@0.9.13@stacksjs/dtsx@0.9.13Cross-reference
Real-world impact: published
@stacksjs/bun-router@0.0.5shipped with broken types. Companion bun-router proposal/cleanup will follow once this is sorted (or worked around).