tauri-plugin-profiling 0.1.0

Tauri plugin for CPU profiling with flamegraph generation
Documentation
#!/usr/bin/env node

/**
 * Build Tool Benchmark for tauri-plugin-profiling
 *
 * Compares build times across: esbuild, tsup, rollup, and vite
 * Each tool is run multiple times to get average build times.
 */

import { execSync } from "node:child_process";
import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
import { join } from "node:path";

const ITERATIONS = 5;
const WARMUP_RUNS = 1;

const tools = [
  { name: "esbuild", cmd: "node esbuild.config.js" },
  { name: "tsup", cmd: "tsup --config bench-js/tsup.config.ts" },
  { name: "rollup", cmd: "rollup -c bench-js/rollup.config.js" },
  { name: "vite", cmd: "vite build --config bench-js/vite.config.ts" },
];

function cleanDist() {
  rmSync("dist-js", { recursive: true, force: true });
}

function getOutputSize() {
  if (!existsSync("dist-js")) return 0;
  let total = 0;
  const files = readdirSync("dist-js");
  for (const file of files) {
    const stat = statSync(join("dist-js", file));
    total += stat.size;
  }
  return total;
}

function runBuild(cmd) {
  const start = performance.now();
  try {
    execSync(cmd, { stdio: "pipe", encoding: "utf-8" });
    const end = performance.now();
    return { success: true, time: end - start };
  } catch (error) {
    return { success: false, time: 0, error: error.message };
  }
}

function formatTime(ms) {
  if (ms < 1000) return `${ms.toFixed(0)}ms`;
  return `${(ms / 1000).toFixed(2)}s`;
}

function formatSize(bytes) {
  if (bytes < 1024) return `${bytes}B`;
  return `${(bytes / 1024).toFixed(1)}KB`;
}

console.log("╔══════════════════════════════════════════════════════════════╗");
console.log("║       Build Tool Benchmark - tauri-plugin-profiling         ║");
console.log("╠══════════════════════════════════════════════════════════════╣");
console.log(
  `  Iterations: ${ITERATIONS} (+ ${WARMUP_RUNS} warmup)                                   `,
);
console.log("╚══════════════════════════════════════════════════════════════╝");
console.log();

const results = [];

for (const tool of tools) {
  process.stdout.write(`Benchmarking ${tool.name}...`);

  // Warmup runs (not counted)
  for (let i = 0; i < WARMUP_RUNS; i++) {
    cleanDist();
    runBuild(tool.cmd);
  }

  const times = [];
  let outputSize = 0;
  let failed = false;

  for (let i = 0; i < ITERATIONS; i++) {
    cleanDist();
    const result = runBuild(tool.cmd);
    if (!result.success) {
      console.log(" FAILED");
      console.log(`  Error: ${result.error?.slice(0, 100)}`);
      failed = true;
      break;
    }
    times.push(result.time);
    if (i === 0) outputSize = getOutputSize();
  }

  if (failed) {
    results.push({ name: tool.name, failed: true });
    continue;
  }

  const avg = times.reduce((a, b) => a + b, 0) / times.length;
  const min = Math.min(...times);
  const max = Math.max(...times);

  results.push({
    name: tool.name,
    avg,
    min,
    max,
    outputSize,
    failed: false,
  });

  console.log(` ${formatTime(avg)} avg`);
}

console.log();
console.log("╔══════════════════════════════════════════════════════════════╗");
console.log("║                          RESULTS                             ║");
console.log("╠══════════════════════════════════════════════════════════════╣");

// Sort by average time
const sorted = results.filter((r) => !r.failed).sort((a, b) => a.avg - b.avg);

const fastest = sorted[0]?.avg || 1;

console.log("║  Tool      │  Avg Time  │  Min     │  Max     │  Size     ║");
console.log("╠════════════╪════════════╪══════════╪══════════╪═══════════╣");

for (const r of sorted) {
  const speedup = r.avg / fastest;
  const speedupStr = speedup === 1 ? "fastest" : `${speedup.toFixed(1)}x`;
  const name = r.name.padEnd(10);
  const avg = formatTime(r.avg).padStart(8);
  const min = formatTime(r.min).padStart(6);
  const max = formatTime(r.max).padStart(6);
  const size = formatSize(r.outputSize).padStart(7);
  console.log(`  ${name}  ${avg}    ${min}    ${max}    ${size}  `);
}

for (const r of results.filter((r) => r.failed)) {
  const name = r.name.padEnd(10);
  console.log(`  ${name}  FAILED        -         -          -     `);
}

console.log("╚══════════════════════════════════════════════════════════════╝");
console.log();

if (sorted.length > 0) {
  const winner = sorted[0];
  const slowest = sorted[sorted.length - 1];
  const speedup = (slowest.avg / winner.avg).toFixed(1);

  console.log(`🏆 Winner: ${winner.name} (${formatTime(winner.avg)} avg)`);
  console.log(
    `   ${speedup}x faster than ${slowest.name} (${formatTime(slowest.avg)} avg)`,
  );
}

// Output markdown table for documentation
console.log();
console.log("## Markdown table for README:");
console.log();
console.log("| Tool | Avg Time | Min | Max | Output Size |");
console.log("|------|----------|-----|-----|-------------|");
for (const r of sorted) {
  console.log(
    `| ${r.name} | ${formatTime(r.avg)} | ${formatTime(r.min)} | ${formatTime(r.max)} | ${formatSize(r.outputSize)} |`,
  );
}