import { describe, it, expect } from "vitest"; import { mkdirSync, mkdtempSync, writeFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Sources, mergeSnippets, fingerprint } from "../src/sources"; import { fingerprint as liveFingerprint } from "../src/live"; import { PREVIEW_IGNORED_DIRS } from "../../core/src/projectFolders"; import { bundleToSingleHtml, type BundleScriptProvenance, } from "../../core/src/compiler/htmlBundler"; describe("source records", () => { it("ignores the generated folders Studio's preview signature ignores", () => { const dir = mkdtempSync(join(tmpdir(), "inspect-source-")); try { writeFileSync(join(dir, "index.html"), "
"); const before = fingerprint(dir); for (const folder of PREVIEW_IGNORED_DIRS) { mkdirSync(join(dir, folder)); writeFileSync(join(dir, folder, "peaks.json"), "[1,2,3]"); } expect(fingerprint(dir)).toBe(before); writeFileSync(join(dir, "data.json"), "{}"); expect(fingerprint(dir)).not.toBe(before); } finally { rmSync(dir, { recursive: true, force: true }); } }); it("does not change when only data-hf-id stamps are added to the html", () => { const dir = mkdtempSync(join(tmpdir(), "inspect-source-")); try { writeFileSync(join(dir, "index.html"), '

Hi

'); const before = liveFingerprint(dir); const standaloneBefore = fingerprint(dir); writeFileSync( join(dir, "index.html"), '

Hi

', ); expect(liveFingerprint(dir)).toBe(before); expect(fingerprint(dir)).not.toBe(standaloneBefore); writeFileSync(join(dir, "index.html"), '

Hi

'); expect(liveFingerprint(dir)).not.toBe(before); } finally { rmSync(dir, { recursive: true, force: true }); } }); it("keeps original multiline blocks and deduplicates targets without reformatting", () => { const dir = mkdtempSync(join(tmpdir(), "inspect-source-")); try { const text = 'const target = "#title";\nconst config = {\n x: 100,\n duration: 1\n};\n'; writeFileSync(join(dir, "scene.js"), text); const source = new Sources(dir); const a = source.slice("scene.js", 24, text.length, "animation creation", 0)!; const b = source.slice("scene.js", 24, text.length, "animation creation", 1)!; const result = mergeSnippets([a, b], source); expect(result).toHaveLength(1); expect(result[0].targets).toEqual([0, 1]); expect(text.includes(result[0].code)).toBe(true); const before = fingerprint(dir); writeFileSync(join(dir, "scene.js"), text.replace("100", "150")); expect(fingerprint(dir)).not.toBe(before); } finally { rmSync(dir, { recursive: true, force: true }); } }); it("extracts raw-text elements with HTML-valid whitespace before the closing bracket", () => { const dir = mkdtempSync(join(tmpdir(), "inspect-raw-text-")); try { writeFileSync( join(dir, "index.html"), "`, ); writeFileSync( join(dir, "mockups/a.html"), `

Mock

`, ); const reads: string[] = []; await bundleToSingleHtml(dir, { onRead: (path) => reads.push(path) }); const files = (sources: Sources) => [...new Set(sources.cssRules.map((rule) => rule.file))].sort(); expect(files(new Sources(dir))).toEqual([ "compositions/scene.html", "mockups/a.html", "theme.css", ]); const served = new Sources(dir, reads); expect(files(served)).toEqual(["compositions/scene.html", "theme.css"]); expect(served.chunks.some((chunk) => chunk.file === "mockups/a.html")).toBe(false); const title = { ref: "r", tag: "h1", id: "title", hfId: null, classes: ["title"], composition: "root", }; expect(new Sources(dir).markup(title, 0)).toBeNull(); expect(served.markup(title, 0)?.file).toBe("index.html"); } finally { rmSync(dir, { recursive: true, force: true }); } }); });