Reproduction link or steps
https://repl.rolldown.rs/ (or minimal repo below)
Source files:
src/types.ts:
export interface Foo {
name: string;
}
src/impl.ts:
export class Bar {
value = 42;
}
src/index.ts:
export { Foo } from './types';
export { Bar } from './impl';
Rolldown config:
module.exports = {
input: { index: 'src/index.ts' },
output: {
dir: 'dist',
preserveModules: true,
preserveModulesRoot: 'src',
sourcemap: true,
},
external: [],
};
What is expected?
Since Foo is a TypeScript interface (type-only, no runtime value), rolldown should fully elide it from the JS output — both the import and the export reference should be removed. The output dist/index.js should be:
import { Bar } from "./impl.js";
export { Bar };
This is how Rollup handles the same scenario.
What is actually happening?
Rolldown correctly strips the import of Foo (since there's no runtime binding to import), but leaves Foo in the export statement, producing:
import { Bar } from "./impl.js";
export { Bar, Foo };
Foo is never imported or defined, so this is invalid JavaScript. Downstream consumers (e.g., webpack) fail with:
Module parse failed: Export 'Foo' is not defined
System Info
rolldown: 1.0.0-rc.11
Node: v22.x
OS: Linux
Any additional comments?
Important notes
- This only affects
export { X } from './file' where X is a pure type (interface or type alias) and the type keyword is not used.
- Using
export type { Foo } from './types' works correctly — rolldown fully strips both the import and export.
- The issue occurs with
preserveModules: true. It may also occur without preserveModules but that was not tested.
- TypeScript does not require the
type keyword on re-exports of type-only symbols (unless isolatedModules + verbatimModuleSyntax are both enabled). Bundlers that process TypeScript (including Rollup, esbuild, and tsc) handle this correctly without the type keyword.
Workaround
Add the type keyword to re-exports of type-only symbols:
export type { Foo } from './types'; // add `type` keyword
export { Bar } from './impl';
Any additional comments?
Related but distinct from:
This bug is specifically about rolldown's TypeScript type elision being incomplete: it strips the import side of a type-only re-export but not the export side, producing invalid output.
Reproduction link or steps
https://repl.rolldown.rs/ (or minimal repo below)
Source files:
src/types.ts:src/impl.ts:src/index.ts:Rolldown config:
What is expected?
Since
Foois a TypeScriptinterface(type-only, no runtime value), rolldown should fully elide it from the JS output — both the import and the export reference should be removed. The outputdist/index.jsshould be:This is how Rollup handles the same scenario.
What is actually happening?
Rolldown correctly strips the import of
Foo(since there's no runtime binding to import), but leavesFooin the export statement, producing:Foois never imported or defined, so this is invalid JavaScript. Downstream consumers (e.g., webpack) fail with:System Info
Any additional comments?
Important notes
export { X } from './file'whereXis a pure type (interface or type alias) and thetypekeyword is not used.export type { Foo } from './types'works correctly — rolldown fully strips both the import and export.preserveModules: true. It may also occur withoutpreserveModulesbut that was not tested.typekeyword on re-exports of type-only symbols (unlessisolatedModules+verbatimModuleSyntaxare both enabled). Bundlers that process TypeScript (including Rollup, esbuild, and tsc) handle this correctly without thetypekeyword.Workaround
Add the
typekeyword to re-exports of type-only symbols:Any additional comments?
Related but distinct from:
export *from externals producing undefined namespace objects (fixed in beta.51)export { type Foo, Bar }(mixed type/value in one statement) being entirely ignored (fixed in chore(rolldown): oxc v0.41.0 #3150)preserveModulesThis bug is specifically about rolldown's TypeScript type elision being incomplete: it strips the import side of a type-only re-export but not the export side, producing invalid output.