Problem
When a library uses tsdown with deps.neverBundle (formerly external) to externalize peer dependencies, the DTS output retains hard import type statements for those packages. Consumers who don't install all externalized packages get TS2307 errors with skipLibCheck: false.
There is currently no way to keep a package external for JS output while inlining its types in DTS output.
Real-world example: unplugin
unplugin externalizes 8 bundler packages (vite, webpack, rollup, esbuild, @farmfe/core, @rspack/core, rolldown, unloader). This is correct for JS — consumers only load the bundler they use at runtime. But the DTS output hard-imports types from all 8, causing TS2307 for any consumer that doesn't install every bundler.
I filed unjs/unplugin#589 about this — the root cause is that there's no DTS-specific dependency configuration in tsdown/rolldown-plugin-dts.
Prior discussion
This was discussed in #106, which was closed with:
This is definitely a goal worth pursuing, though I haven't figured out how to approach it yet.
Requested feature
A way to inline types from specific external packages in DTS output while keeping them external for JS. For example:
// In tsdown config
export default defineConfig({
deps: {
neverBundle: ['webpack', 'vite', ...], // external for JS
},
dts: {
// Hypothetical: inline these packages' types in .d.ts
// even though they're external for JS
resolve: true,
// or: resolve: ['webpack', '@rspack/core', ...]
},
})
Or alternatively, have rolldown-plugin-dts respect Rolldown's external option by default (as #106 suggested) — packages not marked external get their types inlined, packages marked external stay as import type.
The key insight is that JS externalization and DTS externalization serve different purposes:
- JS external: "don't bundle this — it'll be resolved at runtime" (correct for peer deps)
- DTS external: "keep
import type from this package" (breaks consumers who don't install it)
Impact
This affects any library that:
- Supports multiple frameworks/bundlers as optional peer dependencies
- Uses tsdown with
deps.neverBundle for those packages
- Has consumers with
skipLibCheck: false
unplugin is the most prominent example, but any multi-framework library faces the same issue.
Problem
When a library uses tsdown with
deps.neverBundle(formerlyexternal) to externalize peer dependencies, the DTS output retains hardimport typestatements for those packages. Consumers who don't install all externalized packages get TS2307 errors withskipLibCheck: false.There is currently no way to keep a package external for JS output while inlining its types in DTS output.
Real-world example: unplugin
unplugin externalizes 8 bundler packages (
vite,webpack,rollup,esbuild,@farmfe/core,@rspack/core,rolldown,unloader). This is correct for JS — consumers only load the bundler they use at runtime. But the DTS output hard-imports types from all 8, causing TS2307 for any consumer that doesn't install every bundler.I filed unjs/unplugin#589 about this — the root cause is that there's no DTS-specific dependency configuration in tsdown/rolldown-plugin-dts.
Prior discussion
This was discussed in #106, which was closed with:
Requested feature
A way to inline types from specific external packages in DTS output while keeping them external for JS. For example:
Or alternatively, have rolldown-plugin-dts respect Rolldown's
externaloption by default (as #106 suggested) — packages not marked external get their types inlined, packages marked external stay asimport type.The key insight is that JS externalization and DTS externalization serve different purposes:
import typefrom this package" (breaks consumers who don't install it)Impact
This affects any library that:
deps.neverBundlefor those packagesskipLibCheck: falseunplugin is the most prominent example, but any multi-framework library faces the same issue.