64 lines · 2.1 KB
#!/usr/bin/env bun
/**
 * Lint only the files that changed instead of walking the whole repo.
 * Changed set = committed vs base ref + staged + unstaged + untracked.
 *
 * Usage:
 *   bun run lint:changed                # vs origin/master
 *   bun run lint:changed main           # vs a different base ref
 *   LINT_BASE=HEAD~3 bun run lint:changed
 *   bun run lint:changed origin/master --fix   # extra flags forwarded to oxlint
 */
import { execFileSync, spawnSync } from "node:child_process";
import { existsSync } from "node:fs";

const LINTABLE = /\.(cjs|mjs|jsx?|tsx?)$/;

function git(args: string[]): string {
  return execFileSync("git", args, { encoding: "utf8" });
}

const args = process.argv.slice(2);
const baseArg = args.find((a) => !a.startsWith("-"));
const passthrough = args.filter((a) => a !== baseArg);
const base = baseArg ?? process.env.LINT_BASE ?? "origin/master";

let diffBase = base;
try {
  diffBase = git(["merge-base", base, "HEAD"]).trim() || base;
} catch {
  // Keep `base` as-is; the diff below will surface an actionable error.
}

const sources = new Set<string>();
const collect = (out: string): void => {
  for (const line of out.split("\n")) {
    const file = line.trim();
    if (file && LINTABLE.test(file)) sources.add(file);
  }
};