fix(javascript): qualify export helper calls before argument scanning

(cherry picked from commit 73c8b36615)
This commit is contained in:
N0zoM1z0
2026-10-10 23:22:44 +08:00
parent 258ecd361b
commit bcc3ad6014
2 changed files with 23 additions and 9 deletions
@@ -10,6 +10,7 @@ it.each([
'module["exports.value"] = 1;',
"module.exports = { [key]: 1 };",
'Object[method](exports, "value", {});',
"function d() {} d(exports, { invented: 1 });",
])("does not invent an export name for %s", (source) => {
expect(collectJavaScriptExports(parse(source)).values).toEqual([]);
});
@@ -30,6 +31,13 @@ it("retains static computed and ordinary names", () => {
).toEqual(["done", "ready"]);
});
it("retains member helper exports on this", () => {
expect(
collectJavaScriptExports(parse("this.d(exports, { ready: () => 1 });"))
.values,
).toEqual(["ready"]);
});
it("does not expose invented names in bundler module analysis", () => {
const analysis = analyzeJavaScriptStaticSource(`
globalThis.webpackChunkApp.push([[1], { 1: function(module, exports) {
@@ -96,6 +96,12 @@ const collectCallExports = (
): void => {
const callee = memberPath(call.callee);
if (callee === null) return;
const definePropertyCall =
callee.length === 2 &&
callee[0] === "Object" &&
callee[1] === "defineProperty";
const declarationCall = callee.length >= 2 && callee.at(-1) === "d";
if (!definePropertyCall && !declarationCall) return;
const target = memberPath(call.arguments[0]);
const exportTarget =
target !== null &&
@@ -104,15 +110,11 @@ const collectCallExports = (
target[0] === "module" &&
target[1] === "exports"));
if (!exportTarget) return;
if (
callee.length === 2 &&
callee[0] === "Object" &&
callee[1] === "defineProperty"
) {
if (definePropertyCall) {
const name = stringValue(call.arguments[1]);
if (name !== undefined) addExport(output, name);
return;
}
if (callee.at(-1) !== "d") return;
const declarations = call.arguments[1];
if (t.isObjectExpression(declarations))
collectObjectKeys(declarations, output);
@@ -151,7 +153,11 @@ const memberPath = (
properties.push(property);
current = current.object;
}
if (current === undefined || current === null || !t.isIdentifier(current))
return null;
return [current.name, ...properties.reverse()];
if (current === undefined || current === null) return null;
const root = t.isIdentifier(current)
? current.name
: t.isThisExpression(current)
? "this"
: null;
return root === null ? null : [root, ...properties.reverse()];
};