Skip to content

export * loses type modifiers when flattening re-exports #95

Description

@karlhorky

Originally discovered in listr2 issue: listr2/listr2#748 (comment)

Also reported in rollup-plugin-dts: Swatinem/rollup-plugin-dts#354

Describe the bug

When using export * from './foo' in a source file, type-only exports from ./foo lose their type modifier in the bundled declaration output. This causes types to be emitted as value exports in the generated d.ts, which breaks with classes under verbatimModuleSyntax because they appear as runtime symbols.

index.ts

export * from "./interfaces";

interfaces.ts

export { type Task, type TaskWrapper } from "./tasks";

tasks.ts

export class TaskWrapper {
  run(): void {}
}

export interface Task {
  title: string;
}

Output (dist/index.d.ts):

//#region tasks.d.ts
declare class TaskWrapper {
  run(): void;
}
interface Task {
  title: string;
}
//#endregion
export { Task, TaskWrapper };

(no type keyword in TaskWrapper export)

Image

Then, in a consumer, importing TaskWrapper with auto-import in VS Code will not use a type keyword and will not report any TS errors with verbatimModuleSyntax: true. Using Node.js type stripping also yields a syntax error:

consumer.ts

import { TaskWrapper } from "./dist/index.js";
$ node consumer.ts
file:///project/workspace/consumer.ts:1
import { TaskWrapper } from "./dist/index.js";
         ^^^^^^^^^^^
SyntaxError: The requested module './dist/index.js' does not provide an export named 'TaskWrapper'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:228:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:337:5)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:651:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)

Node.js v22.19.0
Image

This led to a problem with listr2 because ListrTaskWrapper is not reported as an error with verbatimModuleSyntax: true, even though it's actually a type-only export:

import { ListrTask /* ✅ error as expected with `verbatimModuleSyntax: true` */ } from 'listr2';
import { ListrTaskWrapper, /* 💥 no error as expected with `verbatimModuleSyntax: true` */ } from 'listr2';

TypeScript Playground

Image

rolldown-plugin-dts should emit a type modifier for the export when there has been export type { ... } or export { type ... } used somewhere along with export *

Reproduction

https://codesandbox.io/p/devbox/hzmx8h

System Info

System:
    OS: Linux 6.1 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
    CPU: (4) x64 AMD EPYC
    Memory: 6.40 GB / 8.01 GB
    Container: Yes
    Shell: Unknown
  Binaries:
    Node: 22.19.0 - /usr/local/bin/node
    Yarn: 1.22.22 - /usr/local/bin/yarn
    npm: 10.9.3 - /usr/local/bin/npm

Used Package Manager

npm

Validations

  • Follow our Code of Conduct
  • Read the Contributing Guide.
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
  • The provided reproduction is a minimal reproducible of the bug.

Contributions

  • I am willing to submit a PR to fix this issue
  • I am willing to submit a PR with failing tests (actually just go ahead and do it, thanks!)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions