fix(javascript): retain unbound CommonJS semantic imports

Collect unbound literal CommonJS dependencies at the semantic module-link owner, retain null binding names and source provenance, and avoid duplicate links for calls already represented by bindings or re-exports. Preserve distinct nested calls, shadowing exclusions, dynamic binding uncertainty, and existing static asset references.

Fixes #1793.
This commit is contained in:
Ysopwnable authored and GitHub committed 2026-10-11 19:16:32 +08:00
1 parent 40d9c7b836
commit 3a17882cc4
10 files changed
+388 -33

No files matched your search

@@ -100,6 +100,7 @@ export const collectSemanticModuleLink = (
);
else if (t.isVariableDeclarator(node)) collectRequireLink(node, state);
else if (t.isAssignmentExpression(node)) collectCommonJsExport(node, state);
else if (t.isCallExpression(node)) collectUnboundRequireLink(node, state);
};
/** Collect lexical references after definitions exist, optionally for one name. */
@@ -322,9 +323,11 @@ const collectRequireLink = (
node: t.VariableDeclarator,
state: JavaScriptSemanticAnalysisState,
): void => {
const origin = semanticRequireOrigin(node.init, state);
if (origin === undefined) return;
for (const binding of requirePatternBindings(node.id, origin.importedPath))
const required = semanticRequireOrigin(node.init, state);
if (required === undefined) return;
const { origin, call } = required;
const bindings = requirePatternBindings(node.id, origin.importedPath);
for (const binding of bindings)
addModuleLink(
state,
{
@@ -337,6 +340,29 @@ const collectRequireLink = (
},
node,
);
if (bindings.length > 0) state.linkedRequireCalls.add(call);
};
const collectUnboundRequireLink = (
node: t.CallExpression,
state: JavaScriptSemanticAnalysisState,
): void => {
if (state.linkedRequireCalls.has(node)) return;
const required = semanticRequireOrigin(node, state);
if (required === undefined) return;
addModuleLink(
state,
{
kind: "require",
specifier: required.origin.specifier,
importedName: null,
localName: null,
exportedName: null,
location: range(node),
},
node,
);
state.linkedRequireCalls.add(node);
};
const requirePatternBindings = (
@@ -382,8 +408,9 @@ const collectCommonJsExport = (
node.operator === "=" && receiverEffect !== "uncertain" ? node.right : null;
while (t.isAssignmentExpression(value, { operator: "=" }))
value = value.right;
const origin =
const required =
value === null ? undefined : semanticRequireOrigin(value, state);
const origin = required?.origin;
addModuleLink(
state,
{
@@ -397,6 +424,7 @@ const collectCommonJsExport = (
},
node,
);
if (required !== undefined) state.linkedRequireCalls.add(required.call);
};
/**