From 32f35650f01c380b84c3d3dcf494ef6f0ad45db9 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 27 Oct 2026 05:49:06 -0500 Subject: [PATCH] Resolve actual extensionless TSX fingerprint dependencies --- .../chat-catalog-fingerprint.test.ts | 20 ++++++++++++++++++- tests/runner-e2e/chat-catalog-fingerprint.ts | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/runner-e2e/chat-catalog-fingerprint.test.ts b/tests/runner-e2e/chat-catalog-fingerprint.test.ts index 1f41a22fb17..5f51aa824f4 100644 --- a/tests/runner-e2e/chat-catalog-fingerprint.test.ts +++ b/tests/runner-e2e/chat-catalog-fingerprint.test.ts @@ -1,4 +1,4 @@ -import { mkdtempSync, writeFileSync, rmSync } from "node:fs"; +import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; @@ -89,3 +89,21 @@ it("replays the retained public name-only identity after raw selected inputs hav expect(chatCatalogCaseFingerprint(descriptor, execution, names).digest).toBe(before); expect(() => chatCatalogCaseFingerprint(descriptor, execution)).toThrow(); }); + +it("binds extensionless TSX components and directory TSX entries", () => { + const f = fixture(); mkdirSync(path.join(f.root, "nested")); + f.write("selected.ts", 'export { value } from "./Component";'); + f.write("Component.tsx", 'export { value } from "./nested";'); + f.write("nested/index.tsx", 'export const value = "actual component";'); + const before = f.fingerprint(); + f.write("nested/index.tsx", 'export const value = "changed component";'); + expect(f.fingerprint()).not.toBe(before); + expect(fingerprintChatDependencyClosure({ root: f.root, entries: ["selected.ts"], selected: {} }).sources.map(row => row.file)) + .toEqual(expect.arrayContaining(["Component.tsx", "nested/index.tsx"])); +}); +it("rejects a missing extensionless component instead of binding an unrelated TSX file", () => { + const f = fixture(); + f.write("selected.ts", 'export { value } from "./MissingComponent";'); + f.write("OtherComponent.tsx", 'export const value = "unrelated";'); + expect(f.fingerprint).toThrow("Unresolved fingerprint dependency: ./MissingComponent"); +}); diff --git a/tests/runner-e2e/chat-catalog-fingerprint.ts b/tests/runner-e2e/chat-catalog-fingerprint.ts index 5c02c08f457..3b2ef639939 100644 --- a/tests/runner-e2e/chat-catalog-fingerprint.ts +++ b/tests/runner-e2e/chat-catalog-fingerprint.ts @@ -15,7 +15,7 @@ const hash = (value: string) => createHash("sha256").update(value).digest("hex") function resolveSource(specifier: string, parent: string): string { const absolute = path.resolve(path.dirname(parent), specifier); const candidates = [absolute.replace(/\.js$/, ".ts"), absolute.replace(/\.js$/, ".tsx"), absolute, - `${absolute}.ts`, path.join(absolute, "index.ts")]; + `${absolute}.ts`, `${absolute}.tsx`, path.join(absolute, "index.ts"), path.join(absolute, "index.tsx")]; const found = candidates.find(candidate => existsSync(candidate) && statSync(candidate).isFile()); if (!found) throw new Error(`Unresolved fingerprint dependency: ${specifier} from ${parent}`); return found;