diff --git a/src/domain/javascriptSemanticAnalysis.empty_keys.test.ts b/src/domain/javascriptSemanticAnalysis.empty_keys.test.ts index 014934066..168b3a36c 100644 --- a/src/domain/javascriptSemanticAnalysis.empty_keys.test.ts +++ b/src/domain/javascriptSemanticAnalysis.empty_keys.test.ts @@ -55,3 +55,51 @@ it("retains exact empty method names and environment keys", () => { { kind: "environment", key: null, resolution: "partial" }, ]); }); + +it("retains exact empty require paths without dynamic-key links", () => { + const ir = analyzeJavaScriptSemantics(` + const { '': fromPattern } = require("./table.js"); + const fromMember = require("./table.js")['']; + const { [key]: dynamicPattern } = require("./table.js"); + const dynamicMember = require("./table.js")[key]; + module.exports.root = require("./table.js")['']; + `); + expect( + ir.moduleLinks.map(({ kind, importedName, localName, exportedName }) => ({ + kind, + importedName, + localName, + exportedName, + })), + ).toEqual([ + { + kind: "require", + importedName: "", + localName: "fromPattern", + exportedName: null, + }, + { + kind: "require", + importedName: "", + localName: "fromMember", + exportedName: null, + }, + { + kind: "commonjs-export", + importedName: "", + localName: null, + exportedName: "root", + }, + ]); +}); + +it("retains exact empty request field names without dynamic fields", () => { + const ir = analyzeJavaScriptSemantics(` + fetch("https://example.test", { '': value, [key]: 1, body: "x" }); + `); + expect( + ir.requestOperations.flatMap(({ fields }) => + fields.map(({ name }) => name), + ), + ).toEqual(["", "body"]); +}); diff --git a/src/domain/javascriptSemanticDataEffects.ts b/src/domain/javascriptSemanticDataEffects.ts index 830d2e94b..e3874fd52 100644 --- a/src/domain/javascriptSemanticDataEffects.ts +++ b/src/domain/javascriptSemanticDataEffects.ts @@ -405,8 +405,8 @@ const requestFields = ( if (!t.isObjectProperty(property)) return []; // `{[key]: 1}` sends no knowable field name; reporting `key` would invent // a request field that may not exist. - const name = semanticStaticPropertyName(property.key, property.computed); - if (name.length === 0) return []; + const name = semanticStaticPropertyKey(property.key, property.computed); + if (name === null) return []; return [ { name, diff --git a/src/domain/javascriptSemanticProjection.ts b/src/domain/javascriptSemanticProjection.ts index c6dc3cd02..4db6d9bf8 100644 --- a/src/domain/javascriptSemanticProjection.ts +++ b/src/domain/javascriptSemanticProjection.ts @@ -25,10 +25,7 @@ import { range, stringValue, } from "./javascriptStaticAnalysisHelpers.js"; -import { - semanticStaticPropertyKey, - semanticStaticPropertyName, -} from "./javascriptAstValues.js"; +import { semanticStaticPropertyKey } from "./javascriptAstValues.js"; interface CollectCallableInput { readonly node: t.Node; @@ -181,8 +178,8 @@ const semanticModuleOrigin = ( const nested = t.isNode(node.object) ? semanticModuleOrigin(node.object, projection, state) : undefined; - const member = semanticStaticPropertyName(node.property, node.computed); - return nested === undefined || member === "" + const member = semanticStaticPropertyKey(node.property, node.computed); + return nested === undefined || member === null ? undefined : { ...nested, importedPath: [...nested.importedPath, member] }; } @@ -341,8 +338,8 @@ const requirePatternBindings = ( if (t.isObjectPattern(pattern)) return pattern.properties.flatMap((property) => { if (t.isRestElement(property)) return []; - const name = semanticStaticPropertyName(property.key, property.computed); - return name === "" + const name = semanticStaticPropertyKey(property.key, property.computed); + return name === null ? [] : requirePatternBindings(property.value, [...path, name]); });