Skip to content

preserveModules emits undeclared named exports for default-imported JSON modules (1.1.3 regression) #10020

Description

@fengmk2

Under preserveModules, a default-imported JSON module is emitted with a named export for each top-level key, but those bindings are never declared, so loading the output fails at ESM link time.

Minimal repro: https://github.com/why-reproductions-are-required/rolldown-preserve-modules-json-repro

// src/index.js
import metrics from "./data.json" with { type: "json" }; // data.json: { "aBeeZee": [...], "Abel": [...] }
export function get(name) { return metrics[name]; }

Build with preserveModules: true, then import("./dist/index.js"):

SyntaxError: Export 'Abel' is not defined in module

The emitted dist/data.js differs from 1.1.2 only in the export line:

-export { data_default as default };                 // 1.1.2 (works)
+export { Abel, aBeeZee, data_default as default };  // 1.1.3 (Abel/aBeeZee never declared)

Abel/aBeeZee exist only as properties of data_default, not as bindings, and aren't used by the importer (it reads the default export dynamically), yet they're emitted and break linking.

Versions: rolldown 1.1.3 (1.1.2 works) · Node v24.18.0

Likely cause: #9934 ("preserve used re-exports under preserveModules"), which emits each preserved module's full declared interface; for a JSON module the per-key "interface" has no backing bindings.

Real-world impact: breaks building cloudflare/vinext (fallback-metrics.ts default-imports fallback-metrics-data.json); consumers' vite.config.ts then crash with Export 'aBeeZee' is not defined in module.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions