diff --git a/AGENTS.md b/AGENTS.md index 2b9e6679c3..be4dd9dce5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -334,7 +334,7 @@ Prettier: single quotes, trailing commas, 100-char width (120 for Markdown) — ### Git hooks - `.husky/commit-msg` → `commitlint --edit` (Conventional Commits enforced) -- `.husky/pre-commit` → `dotgit/hooks/pre-commit-format.sh && dotgit/hooks/pre-commit-reject-binaries.py` +- `.husky/pre-commit` → `dotgit/hooks/pre-commit-format.sh && dotgit/hooks/pre-commit-reject-binaries.py` (the format hook checks only the staged files with Prettier and ESLint, plus the SDK validators when `package/` is staged; CI runs the full `yarn lint`) - Root `postinstall` runs `husky` ### Commits diff --git a/dotgit/hooks/pre-commit-format.sh b/dotgit/hooks/pre-commit-format.sh index 5f81bde242..39adbb8b3f 100755 --- a/dotgit/hooks/pre-commit-format.sh +++ b/dotgit/hooks/pre-commit-format.sh @@ -1,10 +1,61 @@ #!/usr/bin/env bash -set -e +# Lints only the staged files, so a commit takes seconds instead of a full-repo `yarn lint`. +# CI (check-pr.yml) still runs the full `yarn lint`. -if ! yarn lint; then - yarn lint-fix - echo "some files were not formatted correctly (prettier/eslint) commit aborted!" - echo "your changes are still staged, you can accept formatting changes with git add or ignore them by adding --no-verify to git commit" - exit 1 +set -euo pipefail + +ROOT_DIR="$(git rev-parse --show-toplevel)" +cd "$ROOT_DIR" + +PRETTIER="node_modules/.bin/prettier" +ESLINT="node_modules/.bin/eslint" + +# Staged files that still exist (added, copied, modified, renamed). Bash 3.2 has no mapfile. +staged=() +while IFS= read -r -d '' file; do + staged+=("$file") +done < <(git diff --cached --name-only --diff-filter=ACMR -z) + +if [ "${#staged[@]}" -eq 0 ]; then + exit 0 +fi + +lintable=() +touches_package=false +for file in "${staged[@]}"; do + case "$file" in + *.js | *.jsx | *.ts | *.tsx | *.mjs | *.cjs) lintable+=("$file") ;; + esac + case "$file" in + package/*) touches_package=true ;; + esac +done + +run_checks() { + # --ignore-unknown: skip files Prettier has no parser for. .prettierignore still applies. + "$PRETTIER" --list-different --ignore-unknown "${staged[@]}" || return 1 + + if [ "${#lintable[@]}" -gt 0 ]; then + # --no-warn-ignored: files excluded by the ESLint config would otherwise count as warnings. + "$ESLINT" --max-warnings 0 --no-warn-ignored --cache --cache-location node_modules/.cache/eslint/ \ + "${lintable[@]}" || return 1 + fi + + # Both validators only read the SDK package, and take about a second each. + if [ "$touches_package" = true ]; then + yarn workspace stream-chat-react-native-core validate-translations || return 1 + yarn workspace stream-chat-react-native-core validate-entry-isolation || return 1 + fi +} + +if ! run_checks; then + "$PRETTIER" --write --ignore-unknown "${staged[@]}" >/dev/null || true + if [ "${#lintable[@]}" -gt 0 ]; then + "$ESLINT" --fix --no-warn-ignored --cache --cache-location node_modules/.cache/eslint/ \ + "${lintable[@]}" >/dev/null || true + fi + echo "some files were not formatted correctly (prettier/eslint) commit aborted!" + echo "your changes are still staged, you can accept formatting changes with git add or ignore them by adding --no-verify to git commit" + exit 1 fi diff --git a/dotgit/hooks/pre-commit-reject-binaries.py b/dotgit/hooks/pre-commit-reject-binaries.py index efc532d2bc..014bf44ffa 100755 --- a/dotgit/hooks/pre-commit-reject-binaries.py +++ b/dotgit/hooks/pre-commit-reject-binaries.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os import subprocess import sys