<# .SYNOPSIS Build the static site that the Hugging Face Static Space serves. .DESCRIPTION Pipeline: app/ (clean upstream mirror) + overlay/ (static-only additions) -> .build/ (scratch, gitignored) -> next build with output:"export" -> .build/out/ copied to the repository root Hugging Face Static Spaces serve the repository root, so the generated files have to sit there next to README.md. Everything the build emits is recorded in .space-build-manifest and removed before the next copy, so stale hashed chunks never accumulate. .PARAMETER Clean Discard .build entirely first, including cached node_modules. Slower, but the way to rule out a stale-cache problem. .PARAMETER SkipInstall Skip the dependency check. Only valid when .build/node_modules is current. .EXAMPLE .\scripts\build-static.ps1 #> [CmdletBinding()] param( [switch] $Clean, [switch] $SkipInstall ) $ErrorActionPreference = 'Stop' $repoRoot = Split-Path -Parent $PSScriptRoot $appDir = Join-Path $repoRoot 'app' $overlayDir = Join-Path $repoRoot 'overlay' $buildDir = Join-Path $repoRoot '.build' $manifestPath = Join-Path $repoRoot '.space-build-manifest' # Repository files that are never build output. The cleanup step refuses to # touch these even if the manifest somehow names one. $protected = @( '.git', '.github', '.claude', '.build', '.gitignore', '.gitattributes', '.dockerignore', '.space-build-manifest', 'app', 'overlay', 'scripts', 'README.md', 'DEPLOY.md', 'CLAUDE.md', 'Dockerfile', 'UPSTREAM.json', 'dep-repo.md', 'fsv-example.md' ) if (-not (Test-Path -LiteralPath (Join-Path $appDir 'package.json'))) { throw 'app/ is empty or missing. Run .\scripts\sync-upstream.ps1 first.' } # --- Guards: the overlay must stay additive ------------------------------- # _document.tsx is the overlay's injection point precisely because upstream has # none. If upstream adds one, silently shadowing it would drop whatever it does. if (Test-Path -LiteralPath (Join-Path $appDir 'pages/_document.tsx')) { throw @' Upstream now ships pages/_document.tsx, which overlay/pages/_document.tsx would silently replace. Merge upstream's version into overlay/pages/_document.tsx - keep the