import { createHash } from "node:crypto"; import { mkdir, readFile, readlink, symlink, writeFile, } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; import { historicalSourceGraphSchema } from "../../../src/domain/referenceSourceGraph.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { cliTest } from "../../support/cli/cliFixture.js"; cliTest( "retains malformed source bytes without inventing decoded import targets", async ({ cli }) => { const root = await createTestTempDirectory("rea-reference-utf8-"); const sources = new Map([ [ "invalid.mjs", Buffer.concat([ Buffer.from('import "./caf'), Buffer.from([0xff]), Buffer.from('.js";\n'), ]), ], [ "comment.ts", Buffer.concat([ Buffer.from('import "./dep.js"; // '), Buffer.from([0xc0, 0xaf]), ]), ], ["valid.mjs", Buffer.from('import "./caf�.js";\n')], ["caf�.js", Buffer.from("export const value = 1;\n")], ["dep.js", Buffer.from("export const value = 2;\n")], ]); await Promise.all( [...sources].map(([path, bytes]) => writeFile(join(root, path), bytes)), ); const result = await cli.run({ arguments: ["import-reference-source", root, "--json"], environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root }, }); expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe( 0, ); const graph = historicalSourceGraphSchema.parse(result.json); expect(graph.parse_failures).toEqual([ { path: "comment.ts", parser: "utf-8", reason: "Source bytes are not valid UTF-8; import targets were not parsed.", }, { path: "invalid.mjs", parser: "utf-8", reason: "Source bytes are not valid UTF-8; import targets were not parsed.", }, ]); expect(graph.relationships).toEqual([ { from_path: "valid.mjs", to: "caf�.js", kind: "imports", resolution: "internal", parse_state: "parsed", }, ]); for (const [path, bytes] of sources) expect(graph.entries).toContainEqual( expect.objectContaining({ path, sha256: createHash("sha256").update(bytes).digest("hex"), size: bytes.length, content_state: "hashed", }), ); }, ); cliTest.skipIf(process.platform === "win32")( "preserves historical literal-backslash entries and their exact owning import base", async ({ cli }) => { const root = await createTestTempDirectory("rea-reference-literal-paths-"); const config = await createTestTempDirectory( "rea-reference-literal-config-", ); await mkdir(join(root, "literal")); await mkdir(join(root, "literal\\dir")); const sources = new Map([ ["literal\\name.js", "export const value = 'literal';\n"], ["literal/name.js", "export const value = 'slash';\n"], ["literal\\dir/main.js", 'import "./dep.js";\n'], ["literal\\dir/dep.js", "export const value = 'owned';\n"], ]); for (const [path, bytes] of sources) await writeFile(join(root, path), bytes); const result = await cli.run({ arguments: ["import-reference-source", root, "--json"], environment: { HOME: config, XDG_CONFIG_HOME: config, XDG_CACHE_HOME: config, }, }); expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe( 0, ); const graph = historicalSourceGraphSchema.parse(result.json); for (const [path, bytes] of sources) expect(graph.entries).toContainEqual( expect.objectContaining({ path, sha256: createHash("sha256").update(bytes).digest("hex"), content_state: "hashed", }), ); for (const [path, bytes] of sources) expect(await readFile(join(root, path), "utf8")).toBe(bytes); expect(graph.relationships).toContainEqual({ from_path: "literal\\dir/main.js", to: "literal\\dir/dep.js", kind: "imports", resolution: "internal", parse_state: "parsed", }); }, ); cliTest.skipIf(process.platform === "win32")( "preserves an observed historical symlink target with a literal backslash", async ({ cli }) => { const root = await createTestTempDirectory("rea-reference-literal-link-"); const config = await createTestTempDirectory( "rea-reference-literal-link-config-", ); await writeFile( join(root, "literal\\name.js"), "export const value = 42;\n", ); await symlink("literal\\name.js", join(root, "link.js")); const result = await cli.run({ arguments: ["import-reference-source", root, "--json"], environment: { HOME: config, XDG_CONFIG_HOME: config, XDG_CACHE_HOME: config, }, }); expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe( 0, ); const graph = historicalSourceGraphSchema.parse(result.json); expect(await readlink(join(root, "link.js"))).toBe("literal\\name.js"); expect(await readFile(join(root, "literal\\name.js"), "utf8")).toBe( "export const value = 42;\n", ); expect(graph.entries).toContainEqual( expect.objectContaining({ kind: "symlink", path: "link.js", target: "literal\\name.js", target_state: "internal", }), ); }, ); cliTest.skipIf(process.platform === "win32")( "imports an inventory-owned POSIX UNC-looking symlink target", async ({ cli }) => { const root = await createTestTempDirectory("rea-reference-unc-literal-"); const config = await createTestTempDirectory("rea-reference-unc-config-"); const literal = "\\\\server\\share.js"; const literalBytes = "export const value = 'literal';\n"; const slashBytes = "export const value = 'slash';\n"; await mkdir(join(root, "server")); await writeFile(join(root, literal), literalBytes); await writeFile(join(root, "server", "share.js"), slashBytes); await symlink(literal, join(root, "link.js")); const result = await cli.run({ arguments: ["import-reference-source", root, "--json"], environment: { HOME: config, XDG_CONFIG_HOME: config, XDG_CACHE_HOME: config, }, }); expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe( 0, ); const graph = historicalSourceGraphSchema.parse(result.json); expect(graph.entries).toContainEqual( expect.objectContaining({ kind: "symlink", path: "link.js", target: literal, target_state: "internal", }), ); for (const [path, bytes] of [ [literal, literalBytes], ["server/share.js", slashBytes], ]) { expect(graph.entries).toContainEqual( expect.objectContaining({ path, sha256: createHash("sha256").update(bytes!).digest("hex"), }), ); expect(await readFile(join(root, path!), "utf8")).toBe(bytes); } expect(await readlink(join(root, "link.js"))).toBe(literal); }, );