diff --git a/.githooks/README.md b/.githooks/README.md index 709c179648c..3166bdc450c 100644 --- a/.githooks/README.md +++ b/.githooks/README.md @@ -6,6 +6,33 @@ after branch checkout and worktree creation before Git returns. T3 configures the hook path and triggers that checkout once for a fresh clone, before exposing the repository to the user. +## `post-checkout` + +### 1. Clear TypeScript project emit (always on branch / worktree checkout) + +Composite packages and build outputs leave gitignored `dist/` and +`*.tsbuildinfo`. After a branch switch those artifacts can still belong to +another tree while sources follow HEAD. That produces phantom type errors (or +stale emit consumers) that disappear after a clean rebuild — same class of +failure as [scanner#1495](https://github.com/macs-holding/scanner/pull/1495) / +[scanner#2166](https://github.com/macs-holding/scanner/pull/2166) and upstream +typescript-go incremental gaps. + +On every **branch** checkout (flag `1`), this hook deletes: + +- `apps/*/dist`, `apps/*/dist-electron`, `packages/*/dist` +- any remaining `*.tsbuildinfo` outside `node_modules` / `.git` / `.vite-plus` + +File-only checkouts (flag `0`) are left alone. + +**This is a workaround.** Remove when TypeScript/tsgo incremental invalidation +is reliable ([typescript-go#2666](https://github.com/microsoft/typescript-go/issues/2666), +[#4664](https://github.com/microsoft/typescript-go/issues/4664), +[#4262](https://github.com/microsoft/typescript-go/issues/4262)). Do not +re-enable cross-tree dist reuse without an exact source-identity key. + +### 2. Seed copy + pnpm install + Successful reconciliation records the package/lockfile state under `node_modules`, so T3's explicit handoff invocation does not repeat an install which Git already completed. Explicit invocation from the target is required diff --git a/.githooks/post-checkout b/.githooks/post-checkout index 33bb7fd84ed..a3595e610d9 100755 --- a/.githooks/post-checkout +++ b/.githooks/post-checkout @@ -7,10 +7,12 @@ # guest -- not only when a worktree is created through the T3 app / t3.json. # # Strategy (metadata-safe): -# 1. Copy *small* gitignored seeds from .worktreeinclude (CLAUDE.md, .nuxt, …) +# 1. Drop TypeScript project emit (dist / tsbuildinfo) so composite/incremental +# builds never typecheck against foreign-tree .d.ts after a branch switch. +# 2. Copy *small* gitignored seeds from .worktreeinclude (CLAUDE.md, .nuxt, …) # via reflink/hardlink when available. NEVER clone node_modules -- that # creates millions of inodes per worktree on btrfs. -# 2. Populate node_modules with `pnpm install --frozen-lockfile --prefer-offline` +# 3. Populate node_modules with `pnpm install --frozen-lockfile --prefer-offline` # against the shared store (hardlinks -> one inode per package file). # # Idempotent: never overwrites existing seed paths; pnpm is safe to re-run. @@ -26,6 +28,52 @@ main_wt=$(git worktree list --porcelain | sed -n '1s/^worktree //p') wt_root=$(git rev-parse --show-toplevel 2>/dev/null) [ -n "$wt_root" ] || exit 0 +# --- TS build-cache wipe (every branch / worktree checkout) ----------------- +# +# Composite/incremental emit (and leftover dist from builds) can outlive a +# branch switch. Foreign .tsbuildinfo / .d.ts produce phantom type errors that +# vanish after clean rebuild — same class as macs-holding/scanner#1495 / #2166 +# and typescript-go incremental gaps (#2666, #4664, #4262). +# Workaround until upstream invalidation is solid; see .githooks/README.md. +# +clean_ts_project_emit() { + root=$1 + removed=0 + + for d in \ + "$root"/apps/*/dist \ + "$root"/apps/*/dist-electron \ + "$root"/packages/*/dist + do + [ -e "$d" ] || continue + rm -rf "$d" + removed=1 + done + + if [ -d "$root" ]; then + # Prune by basename so nested apps/*/node_modules are skipped too. + tsbuild_files=$( + find "$root" \ + \( -name node_modules -o -name .git -o -name .vite-plus \) -prune -o \ + -type f -name '*.tsbuildinfo' -print 2>/dev/null || true + ) + if [ -n "$tsbuild_files" ]; then + printf '%s\n' "$tsbuild_files" | while IFS= read -r f; do + [ -n "$f" ] || continue + rm -f "$f" + done + removed=1 + fi + fi + + if [ "$removed" = 1 ]; then + echo "post-checkout: cleared TypeScript project emit (dist / tsbuildinfo) under $root" >&2 + echo "post-checkout: workaround for tsgo/tsc stale incremental cache after tree switch — see .githooks/README.md (remove when upstream fixed)" >&2 + fi +} + +clean_ts_project_emit "$wt_root" + if [ -n "$main_wt" ] && [ "$main_wt" != "$wt_root" ]; then # Effective include: repo .worktreeinclude if present, else nothing to seed-copy. include=""