chore: add lint check to pre-commit hook
- Add scripts/lint-staged.js to run oxlint on staged files - Extend format-staged.js to include ui/src/ directory - Update pre-commit hook to run both format and lint checks The pre-commit hook now blocks commits if there are linting errors, catching issues like unused imports before they land.
This commit is contained in:
parent
2d451b30f9
commit
13661abbd8
@ -1,4 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||||
[ -z "$ROOT" ] && exit 0
|
[ -z "$ROOT" ] && exit 0
|
||||||
node "$ROOT/scripts/format-staged.js"
|
|
||||||
|
# Format staged files
|
||||||
|
node "$ROOT/scripts/format-staged.js" || exit 1
|
||||||
|
|
||||||
|
# Lint staged files
|
||||||
|
node "$ROOT/scripts/lint-staged.js" || exit 1
|
||||||
|
|||||||
@ -61,6 +61,7 @@
|
|||||||
"LICENSE",
|
"LICENSE",
|
||||||
"scripts/postinstall.js",
|
"scripts/postinstall.js",
|
||||||
"scripts/format-staged.js",
|
"scripts/format-staged.js",
|
||||||
|
"scripts/lint-staged.js",
|
||||||
"scripts/setup-git-hooks.js",
|
"scripts/setup-git-hooks.js",
|
||||||
"git-hooks/**",
|
"git-hooks/**",
|
||||||
"dist/terminal/**",
|
"dist/terminal/**",
|
||||||
|
|||||||
@ -41,7 +41,7 @@ function filterOxfmtTargets(paths) {
|
|||||||
return paths
|
return paths
|
||||||
.map(normalizeGitPath)
|
.map(normalizeGitPath)
|
||||||
.filter((filePath) =>
|
.filter((filePath) =>
|
||||||
(filePath.startsWith("src/") || filePath.startsWith("test/")) &&
|
(filePath.startsWith("src/") || filePath.startsWith("test/") || filePath.startsWith("ui/src/")) &&
|
||||||
OXFMT_EXTENSIONS.has(path.posix.extname(filePath)),
|
OXFMT_EXTENSIONS.has(path.posix.extname(filePath)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
104
scripts/lint-staged.js
Normal file
104
scripts/lint-staged.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { spawnSync } from "node:child_process";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const LINT_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]);
|
||||||
|
|
||||||
|
function getRepoRoot() {
|
||||||
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
return path.resolve(here, "..");
|
||||||
|
}
|
||||||
|
|
||||||
|
function runGitCommand(args, options = {}) {
|
||||||
|
return spawnSync("git", args, {
|
||||||
|
cwd: options.cwd,
|
||||||
|
encoding: "utf-8",
|
||||||
|
stdio: options.stdio ?? "pipe",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitNullDelimited(value) {
|
||||||
|
if (!value) return [];
|
||||||
|
const text = String(value);
|
||||||
|
return text.split("\0").filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeGitPath(filePath) {
|
||||||
|
return filePath.replace(/\\/g, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterLintTargets(paths) {
|
||||||
|
return paths
|
||||||
|
.map(normalizeGitPath)
|
||||||
|
.filter((filePath) =>
|
||||||
|
(filePath.startsWith("src/") ||
|
||||||
|
filePath.startsWith("test/") ||
|
||||||
|
filePath.startsWith("ui/src/")) &&
|
||||||
|
LINT_EXTENSIONS.has(path.posix.extname(filePath)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveOxlintCommand(repoRoot) {
|
||||||
|
const binName = process.platform === "win32" ? "oxlint.cmd" : "oxlint";
|
||||||
|
const local = path.join(repoRoot, "node_modules", ".bin", binName);
|
||||||
|
if (fs.existsSync(local)) {
|
||||||
|
return { command: local, args: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = spawnSync("oxlint", ["--version"], { stdio: "ignore" });
|
||||||
|
if (result.status === 0) {
|
||||||
|
return { command: "oxlint", args: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGitPaths(args, repoRoot) {
|
||||||
|
const result = runGitCommand(args, { cwd: repoRoot });
|
||||||
|
if (result.status !== 0) return [];
|
||||||
|
return splitNullDelimited(result.stdout ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function lintFiles(repoRoot, oxlint, files) {
|
||||||
|
const result = spawnSync(oxlint.command, [...oxlint.args, ...files], {
|
||||||
|
cwd: repoRoot,
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
return result.status === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const repoRoot = getRepoRoot();
|
||||||
|
const staged = getGitPaths(
|
||||||
|
["diff", "--cached", "--name-only", "-z", "--diff-filter=ACMR"],
|
||||||
|
repoRoot,
|
||||||
|
);
|
||||||
|
const targets = filterLintTargets(staged);
|
||||||
|
if (targets.length === 0) return;
|
||||||
|
|
||||||
|
const oxlint = resolveOxlintCommand(repoRoot);
|
||||||
|
if (!oxlint) {
|
||||||
|
process.stderr.write("[pre-commit] oxlint not found; skipping lint.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lintFiles(repoRoot, oxlint, targets)) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
filterLintTargets,
|
||||||
|
getRepoRoot,
|
||||||
|
normalizeGitPath,
|
||||||
|
resolveOxlintCommand,
|
||||||
|
splitNullDelimited,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
process.argv[1] &&
|
||||||
|
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
||||||
|
) {
|
||||||
|
main();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user