import { createHash } from "node:crypto"; import { mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect, it } from "vitest"; import { reconstructJavaScriptArtifact } from "../../../src/application/javascript/JavaScriptArtifactReconstruction.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; const cases = [ ['', []], ['', []], [``, []], ["", ["actual.js"]], ['', ["actual.js"]], ['', ["actual.js"]], ["", ["actual.js"]], ['', ["actual.js"]], ['', []], ['', []], ['', []], ['', ["actual.js"]], ] as const; it.each(cases)( "projects local loads from actual HTML attributes in %s", async (html, expected) => { const root = await createTestTempDirectory("rea-html-entrypoints-"); await Promise.all([ writeFile(join(root, "index.html"), html), writeFile(join(root, "actual.js"), "globalThis.ownedActual = true;"), writeFile(join(root, "decoy.js"), "globalThis.ownedDecoy = true;"), ]); const result = await reconstructJavaScriptArtifact({ input_path: root }); const loads = result.graph.edges.filter( ({ relation, properties }) => relation === "loads" && properties.script_path !== undefined, ); expect(loads.map(({ properties }) => properties.resolved_path)).toEqual( expected, ); }, ); it("preserves source digest, opening-tag range and decoded local base resolution", async () => { const root = await createTestTempDirectory("rea-html-entrypoint-evidence-"); const opening = '`; await mkdir(join(root, "assets")); await Promise.all([ writeFile(join(root, "index.html"), html), writeFile( join(root, "assets", "actual.js"), "globalThis.ownedActual = true;", ), ]); const result = await reconstructJavaScriptArtifact({ input_path: root }); const loads = result.graph.edges.filter( ({ relation, properties }) => relation === "loads" && properties.script_path !== undefined, ); expect(loads).toHaveLength(1); expect(loads[0]).toMatchObject({ properties: { script_path: "actual.js", base_href: "assets/", resolved_path: "assets/actual.js", }, evidence: { authority: "static-relationship-inference", artifact: { available: true, sha256: createHash("sha256").update(html).digest("hex"), }, location: { available: true, value: { kind: "source-range", source: "index.html", start: { line: 2, column: 0 }, end: { line: 2, column: opening.length }, }, }, }, }); });