diff --git a/src/domain/javascript/javascriptAstExports.exact_keys.test.ts b/src/domain/javascript/javascriptAstExports.exact_keys.test.ts index 0a1bd40888..b843e2ad4c 100644 --- a/src/domain/javascript/javascriptAstExports.exact_keys.test.ts +++ b/src/domain/javascript/javascriptAstExports.exact_keys.test.ts @@ -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) { diff --git a/src/domain/javascript/javascriptAstFingerprint.ts b/src/domain/javascript/javascriptAstFingerprint.ts index 14dbdd7192..c8b7cb1e9c 100644 --- a/src/domain/javascript/javascriptAstFingerprint.ts +++ b/src/domain/javascript/javascriptAstFingerprint.ts @@ -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()]; };