LibJS: Re-export imported module namespaces indirectly

`import * as ns; export { ns }` was being recorded as a local
export, so two star exports of matching namespace objects resolved
through different fixture modules and became ambiguous.

Emit the same indirect namespace export metadata used by `export * as`
for both normal compilation and bytecode-cache materialization. Add a
module test that merges two matching namespace re-exports.
This commit is contained in:
Andreas Kling 2026-05-21 15:29:42 +02:00 committed by Andreas Kling
parent bdecc7f558
commit 94fea5ad2b
8 changed files with 26 additions and 5 deletions

View file

@ -1950,7 +1950,12 @@ fn collect_module_imports_and_exports(scope: &ast::ScopeData, metadata: &mut Mod
.find(|import| entry.local_or_import_name.as_ref() == Some(&import.local_name));
if let Some(import_entry) = matching_import {
if import_entry.import_name.is_none() {
metadata.local_exports.push(export_record(entry, None));
metadata.indirect_exports.push(ModuleExportEntryRecord {
kind: ast::ExportEntryKind::ModuleRequestAll,
export_name: entry.export_name.clone(),
local_or_import_name: None,
module_request: Some(import_entry.module_request.clone()),
});
} else {
metadata.indirect_exports.push(ModuleExportEntryRecord {
kind: entry.kind,

View file

@ -2048,14 +2048,14 @@ unsafe fn extract_module_metadata(scope: &ast::ScopeData, ctx: *mut c_void, cb:
if let Some(import_entry) = matching_import {
if import_entry.import_name.is_none() {
// Namespace re-export → local export.
// Re-export of an imported module namespace object becomes an indirect namespace export.
call_export_callback(
cb.push_local_export,
cb.push_indirect_export,
ctx,
entry.kind as u8,
ExportEntryKind::ModuleRequestAll as u8,
entry.export_name.as_ref(),
entry.local_or_import_name.as_ref(),
None,
Some(&import_entry.module_request),
);
} else {
// Re-export of a specific binding → indirect export.

View file

@ -208,6 +208,10 @@ describe("in- and exports", () => {
expectModulePassed("./re-export-namespace-via-binding.mjs");
});
test("can merge matching namespace re-exports", () => {
expectModulePassed("./namespace-re-export-entry.mjs");
});
test("import variable before import statement behaves as undefined and non mutable variable", () => {
expectModulePassed("./accessing-var-import-before-decl.mjs");
});

View file

@ -0,0 +1,3 @@
import * as foo from "./namespace-re-export-source.mjs";
export { foo };

View file

@ -0,0 +1,3 @@
import * as foo from "./namespace-re-export-source.mjs";
export { foo };

View file

@ -0,0 +1,3 @@
import { foo } from "./namespace-re-export-star.mjs";
export const passed = typeof foo === "object";

View file

@ -0,0 +1 @@
export const source = 1;

View file

@ -0,0 +1,2 @@
export * from "./namespace-re-export-a.mjs";
export * from "./namespace-re-export-b.mjs";