srcmetrics-cli 0.2.0

Command-line tool and local HTTP API for srcmetrics: analyze source code, export metrics as JSON/CSV, statistics and HTML reports
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>srcmetrics</title>
<style>
  :root { --bg: #fff; --fg: #222; --border: #ccc; --error: #b00020; }
  @media (prefers-color-scheme: dark) { :root { --bg: #1b1b1b; --fg: #ddd; --border: #555; --error: #ff6b6b; } }
  body { font-family: system-ui, sans-serif; margin: 16px; background: var(--bg); color: var(--fg); }
  textarea { width: 100%; height: 16em; font-family: ui-monospace, monospace; box-sizing: border-box; }
  input, textarea, button { background: var(--bg); color: var(--fg); border: 1px solid var(--border); }
  table { border-collapse: collapse; font-size: 13px; margin: 8px 0; }
  td, th { border: 1px solid var(--border); padding: 2px 8px; text-align: right; }
  td:first-child, th:first-child { text-align: left; }
  .error { color: var(--error); }
  .tables { overflow-x: auto; }
</style>
</head>
<body>
<h1>srcmetrics</h1>
<p>Paste source code and give it a file name; the extension selects the language.</p>
<p><label>File name <input id="filename" value="example.py"></label>
<button id="analyze">Analyze</button></p>
<textarea id="source" spellcheck="false">def greet(name):
    if name:
        return "Hello, " + name
    return "Hello"
</textarea>
<div id="output" class="tables"></div>
<script>
const output = document.getElementById("output");

function cell(tag, text) {
  const element = document.createElement(tag);
  element.textContent = text;
  return element;
}

function table(title, columns, rows) {
  const section = document.createElement("section");
  section.appendChild(cell("h2", title));
  const t = document.createElement("table");
  const header = document.createElement("tr");
  columns.forEach(c => header.appendChild(cell("th", c)));
  t.appendChild(header);
  rows.forEach(r => {
    const tr = document.createElement("tr");
    r.forEach(v => tr.appendChild(cell("td", v)));
    t.appendChild(tr);
  });
  section.appendChild(t);
  return section;
}

function show(value, reason) {
  if (value !== null) return Number.isInteger(value) ? String(value) : value.toFixed(3);
  return "null (" + reason + ")";
}

document.getElementById("analyze").addEventListener("click", async () => {
  output.replaceChildren(cell("p", "Analyzing…"));
  const response = await fetch("/api/analyze", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      filename: document.getElementById("filename").value,
      source: document.getElementById("source").value,
    }),
  });
  const body = await response.json();
  if (!response.ok) {
    const p = cell("p", body.error);
    p.className = "error";
    output.replaceChildren(p);
    return;
  }
  const file = body.files[0];
  const fileRows = Object.entries(file.metrics).map(([id, v]) => [id, show(v, file.unavailable[id])]);
  const functions = file.functions;
  const ids = functions.length ? Object.keys(functions[0].metrics) : [];
  const functionRows = ids.map(id => [id, ...functions.map(f => show(f.metrics[id], f.unavailable[id]))]);
  const names = functions.map(f => (f.name ?? "(anonymous)") + " :" + f.start_line);
  output.replaceChildren(
    table("File", ["metric", "value"], fileRows),
    table("Functions", ["metric", ...names], functionRows),
  );
});
</script>
</body>
</html>