import { CodeCovRender, loadCodeCoverage } from "./code_coverage.ts"; import * as data from "./data.ts"; import { loadFlameGraph } from "./flamegraph.ts"; function countLines(code: string) { let lines = 0; for (const char of code) { if (char === "\n") lines += 1; } return lines; } function createLineElement(code: string): HTMLPreElement { const lines = countLines(code) + 1; const maxLineWidth = lines.toString().length; let text = ""; for (let i = 1; i < lines; ++i) { const node = i.toString().padStart(maxLineWidth); text += node; text += "\n"; } const lineElement = document.createElement("pre"); lineElement.classList.add("code-lines"); lineElement.innerText = text; return lineElement; } async function checkStatus(): Promise<"running" | "done"> { const status = await data.status(); if (status.running) { return "running"; } const statusHtml = document.querySelector( "#status", )!; statusHtml.innerText = "Done"; document.body.classList.remove("status-waiting"); document.body.classList.add("status-done"); return "done"; } function sourceCode(view: Element, codeData: string) { const outerContainer = document.createElement("div"); outerContainer.classList.add("code-container"); const innerContainer = document.createElement("div"); innerContainer.classList.add("code-container-inner"); const lines = createLineElement(codeData); const code = document.createElement("pre"); code.classList.add("code-source"); code.innerText = codeData; innerContainer.append(lines, code); outerContainer.append(innerContainer); view.replaceChildren(outerContainer); } function createRadio( id: string, content: string, checked: boolean, ): [HTMLDivElement, HTMLInputElement] { const label = document.createElement("label"); label.htmlFor = id; label.innerText = content; const input = document.createElement("input"); input.id = id; input.name = "coverage-radio"; input.type = "radio"; input.hidden = true; input.checked = checked; const container = document.createElement("div"); container.classList.add("coverage-radio-group"); container.append(input, label); return [container, input]; } async function codeCoverage(view: Element, codeData: string) { const codeCoverageData = await data.codeCoverageData(); const outerContainer = document.createElement("div"); outerContainer.classList.add("code-container"); outerContainer.classList.add("code-coverage"); const innerContainer = document.createElement("div"); innerContainer.classList.add("code-container-inner"); const [perfGroup, perfInput] = createRadio( "performance-coverage", "Performance view", true, ); const [testGroup, testInput] = createRadio( "test-coverage", "Test view", false, ); function load(mode: CodeCovRender, tooltip: HTMLDivElement) { const lines = createLineElement(codeData); const code = loadCodeCoverage( codeData, codeCoverageData, tooltip, mode, ); innerContainer.replaceChildren(lines, code); } const radios = document.createElement("div"); radios.append(perfGroup, testGroup); radios.classList.add("coverage-radio"); const tooltip = document.createElement("div"); tooltip.id = "covers-tooltip"; tooltip.hidden = true; outerContainer.append(innerContainer); view.replaceChildren(outerContainer, tooltip, radios); if (perfInput.checked) { load("performance", tooltip); } else if (testInput.checked) { load("test-coverage", tooltip); } perfInput.addEventListener("input", () => load("performance", tooltip)); testInput.addEventListener("input", () => load("test-coverage", tooltip)); } async function main() { type RenderFns = { "source-code": () => void; "code-coverage": () => void; "flame-graph": () => void; }; const codeData = await data.codeData(); const view = document.querySelector("#view")!; const renderFunctions: RenderFns = { "source-code": () => sourceCode(view, codeData), "code-coverage": async () => await codeCoverage(view, codeData), "flame-graph": async () => { const flameGraphData = await data.flameGraphData(); const flameGraphFnNames = await data.flameGraphFnNames(); const container = document.createElement("div"); container.classList.add("flame-graph"); container.id = "flame-graph"; const view = document.querySelector("#view")!; view.replaceChildren(container); loadFlameGraph(flameGraphData, flameGraphFnNames, container); }, }; const viewRadios: NodeListOf = document.querySelectorAll( 'input[name="views"]', ); for (const input of viewRadios) { input.addEventListener("input", (ev) => { const target = ev.target as HTMLInputElement; const value = target.value as keyof RenderFns; renderFunctions[value](); }); if (input.checked) { const value = input.value as keyof RenderFns; renderFunctions[value](); } } checkStatus().then((status) => { if (status == "done") { return; } const interval = setInterval(async () => { const status = await checkStatus(); if (status == "done") { clearInterval(interval); } }, 500); }); } main();