fix(parser): preserve default import names in reexports (#28442)

Record `default` as the imported name when reexporting a default import,
including aliases and exports declared before the import.

Validated with scoped Rust checks, full conformance, parser bindings,
allocation tracking.

AI-assisted (Codex).
This commit is contained in:
Boshen authored and GitHub committed 2026-10-30 11:36:39 +08:00
1 parent 10e9fc7f81
commit 342151609b
2 files changed
+32 -7

No files matched your search

+31 -6
View File
@@ -163,12 +163,9 @@ impl<'a> ModuleRecordBuilder<'a> {
ImportImportName::Name(name) => {
ExportImportName::Name(name.clone())
}
// `import d from "mod"`
// `export { d }`
// ^ this is local_name of ie
ImportImportName::Default(_) => {
ExportImportName::Name(ie.local_name.clone())
}
ImportImportName::Default(span) => ExportImportName::Name(
NameSpan::new("default".into(), *span),
),
ImportImportName::NamespaceObject => unreachable!(),
},
export_name: ee.export_name.clone(),
@@ -714,6 +711,34 @@ mod module_record_tests {
assert_eq!(module_record.indirect_export_entries[0], export_entry);
}
#[test]
fn reexport_imported_default() {
for (source, exported) in [
("import d from 'mod'; export { d };", "d"),
("export { d as renamed }; import d from 'mod';", "renamed"),
("import d from 'mod'; export { d as default };", "default"),
] {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source, SourceType::mjs()).parse();
assert!(ret.diagnostics.is_empty());
assert!(ret.module_record.local_export_entries.is_empty());
assert_eq!(ret.module_record.indirect_export_entries.len(), 1);
let entry = &ret.module_record.indirect_export_entries[0];
let import_start = u32::try_from(source.find("import d").unwrap()).unwrap() + 7;
assert_eq!(
entry.import_name,
ExportImportName::Name(NameSpan::new(
"default".into(),
Span::sized(import_start, 1),
))
);
let ExportExportName::Name(name) = &entry.export_name else { panic!() };
assert_eq!(name.name, exported);
assert_eq!(entry.module_request.as_ref().unwrap().name, "mod");
assert!(matches!(entry.local_name, ExportLocalName::Null));
}
}
#[test]
fn indirect_export_entries() {
let allocator = Allocator::default();
+1 -1
View File
@@ -801,7 +801,7 @@ describe("UTF-16 span", () => {
"importName": {
"end": 13,
"kind": "Name",
"name": "x",
"name": "default",
"start": 12,
},
"isType": false,