fix(javascript): preserve lexical binding and default export capture semantics

Resolve CommonJS wrapper bindings from the parser-owned source type. Capture default identifier export values at the export statement while preserving live named bindings.

Refs: #3261, #3297

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
This commit is contained in:
morluto
2026-10-14 17:49:13 +08:00
parent 322506d0c0
commit dae1e55381
6 changed files with 111 additions and 16 deletions
@@ -102,7 +102,7 @@ export const collectSemanticModuleLink = (
kind: "export",
specifier: null,
importedName: null,
localName: defaultDeclarationName(node.declaration),
localName: defaultExportLocalName(node.declaration),
exportedName: "default",
callableId: semanticCallableIdForNode(node.declaration),
location: range(node),
@@ -557,6 +557,11 @@ const addModuleLink = (
callableId: link.callableId ?? null,
};
state.moduleLinks.push(collected);
if (
t.isExportDefaultDeclaration(referenceNode) &&
t.isExpression(referenceNode.declaration)
)
state.defaultExportExpressions.set(collected, referenceNode.declaration);
if (link.localName !== null) {
const binding = resolveSemanticBindingState(
state,
@@ -816,13 +821,16 @@ const declarationNames = (declaration: t.Declaration): string[] => {
return [];
};
const defaultDeclarationName = (
const defaultExportLocalName = (
declaration: t.ExportDefaultDeclaration["declaration"],
): string | null =>
(t.isFunctionDeclaration(declaration) || t.isClassDeclaration(declaration)) &&
t.isIdentifier(declaration.id)
? declaration.id.name
): string | null => {
const value = unwrapJavaScriptExpression(declaration).node;
if (t.isIdentifier(value)) return value.name;
return (t.isFunctionDeclaration(value) || t.isClassDeclaration(value)) &&
t.isIdentifier(value.id)
? value.id.name
: null;
};
const commonJsExportName = (
node: t.Node,