From f4360f7477c727c5034547c52888c02e45ad9efe Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 04:55:58 +0000 Subject: [PATCH 1/2] fix(build): type-check with astro-check so a broken install fails loudly When `@astrojs/check` fails to import, `astro check` logs an error and then falls through to process.exit(0) (astro/dist/cli/index.js). In CI it doesn't even prompt: ci-info short-circuits the install offer. So a broken install -- e.g. a missing transitive like @emnapi/core -- turns the type-check gate into a silent no-op on CI and Vercel. `astro:check` is now `astro sync && astro-check`: the bin from @astrojs/check imports it statically, so the same breakage crashes with ERR_MODULE_NOT_FOUND naming the real missing package, and exits 1. The `astro sync` keeps parity with `astro check`, which syncs first; without it a fresh checkout reports 4 false errors. build, check and check:fix call `pnpm run astro:check` so the command is defined once. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011rvCtaiKiC4ZSvX9yewguJ --- .claude/hooks/session-start.sh | 2 +- .github/workflows/ci.yml | 2 +- CLAUDE.md | 15 +++++++++------ README.md | 8 ++++---- package.json | 8 ++++---- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh index 2802e02..4a17e89 100755 --- a/.claude/hooks/session-start.sh +++ b/.claude/hooks/session-start.sh @@ -18,7 +18,7 @@ cd "${CLAUDE_PROJECT_DIR:-$(dirname "$0")/../..}" # script only runs where .tool-versions isn't guaranteed to be self-enforcing # (see the CLAUDE_CODE_REMOTE guard above). On Node 18, pnpm silently skips # the platform-specific @oxc-parser native bindings; the install still exits -# 0 and the failure only appears later as an unrelated-looking `astro check` +# 0 and the failure only appears later as an unrelated-looking `astro sync` # crash. REQUIRED_NODE_MAJOR="$(awk '$1 == "nodejs" { split($2, v, "."); print v[1] }' .tool-versions 2>/dev/null)" REQUIRED_NODE_MAJOR="${REQUIRED_NODE_MAJOR:-22}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e40705..d584c84 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - # `pnpm run build` is `astro check && astro build`, which never invokes + # `pnpm run build` is type-check + `astro build`, which never invokes # ESLint or Prettier. Without these two steps a change that breaks # linting outright — an ESLint major that orphans the config file, say — # still reports a green build. diff --git a/CLAUDE.md b/CLAUDE.md index 3fcaba7..94b65e8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,7 @@ Cannot find module '@oxc-parser/binding-linux-x64-gnu' ``` Because that happens inside Astro's content-collection sync, the visible -symptom is `astro check` aborting about two seconds in — which reads like a +symptom is `astro sync` aborting about two seconds in — which reads like a config or content error, not a dependency error. Quick check that your install is intact: @@ -57,12 +57,12 @@ ls node_modules/@oxc-parser/ # must NOT be empty |---|---| | `pnpm install --frozen-lockfile` | Install exactly what the lockfile specifies | | `pnpm run dev` | Dev server at `localhost:4321` | -| `pnpm run build` | **`astro check && astro build`** — type errors fail the build | +| `pnpm run build` | **`astro:check` then `astro build`** — type errors fail the build | | `pnpm run preview` | Serve the production build locally | | `pnpm run astro:check` | Type-check only, no build | | `pnpm run lint` / `lint:fix` | ESLint | | `pnpm run format` / `format:check` | Prettier, plus straightening curly quotes in Markdown | -| `pnpm run check` | `astro check` + lint + format check | +| `pnpm run check` | `astro:check` + lint + format check | | `pnpm run check:fix` | Same, but writes fixes | | `pnpm new:post` | Scaffold a blog post (interactive, or `--title "..."`; `--help` for flags) | @@ -167,9 +167,12 @@ font from a third-party demo host at module scope, which made every production build depend on an unrelated service's uptime. Fonts now come from `public/fonts/`. Don't reintroduce build-time fetches — vendor the asset. -**`astro check` gates the build.** `pnpm run build` runs it first, so a type -error anywhere blocks deployment. Run `pnpm run astro:check` while iterating for -a faster loop. +**Type-checking gates the build.** `pnpm run build` runs `astro:check` first, so +a type error anywhere blocks deployment. That script is `astro sync && astro-check`, +not `astro check`: when `@astrojs/check` fails to import, `astro check` logs an +error and still exits 0, so a broken install silently skips type-checking. +`astro-check` crashes instead. Keep the `astro sync` — without it a fresh checkout +reports false errors. **OG images are generated at build time** by satori + resvg for every post. Changing the OG templates re-renders ~215 PNGs, so image-heavy changes are slow diff --git a/README.md b/README.md index e83bb8f..96c51d8 100644 --- a/README.md +++ b/README.md @@ -86,13 +86,13 @@ Based on the original Brutal theme with our enhancements: | Command | Action | Notes | |---------|--------|-------| | `pnpm run dev` | Start local dev server at `localhost:4321` | Enhanced with TypeScript support | -| `pnpm run build` | Build production site to `./dist/` | Runs `astro check` first — type errors fail the build | +| `pnpm run build` | Build production site to `./dist/` | Runs `astro:check` first — type errors fail the build | | `pnpm run preview` | Preview build locally before deploying | Test production build | | `pnpm run astro` | Run Astro CLI commands | Full CLI access | | `pnpm run astro:check` | Type-check without building | Faster iteration loop | | `pnpm run lint` / `lint:fix` | ESLint | `:fix` writes fixes | | `pnpm run format` / `format:check` | Prettier | `format:check` is read-only | -| `pnpm run check` / `check:fix` | `astro check` + lint + format | Full pre-commit sweep | +| `pnpm run check` / `check:fix` | `astro:check` + lint + format | Full pre-commit sweep | | `pnpm new:post` | Create a new blog post with frontmatter | Interactive, or pass `--title`; `--help` for flags | A full build takes roughly 70-110 seconds and emits ~215 pages, including a @@ -255,7 +255,7 @@ GitHub repository, builds on every push to `main`, and publishes to #### **GitHub Actions is a build gate, not a deploy path** `.github/workflows/ci.yml` installs dependencies and runs `pnpm run build` -(`astro check && astro build`) on pushes and pull requests to `main`. That's +(type-check, then `astro build`) on pushes and pull requests to `main`. That's all it does. If a change doesn't build, it fails here before Vercel picks it up. An earlier version of this workflow also deployed to Vercel via @@ -302,7 +302,7 @@ graph TD A[Push to main] --> B[GitHub Actions: ci.yml] C[Open Pull Request] --> B B --> D[pnpm install --frozen-lockfile] - D --> E[pnpm run build
astro check && astro build] + D --> E[pnpm run build
astro sync, astro-check, astro build] E --> F{Build passes?} F -->|No| G[Red check, stop] F -->|Yes| H[Green check] diff --git a/package.json b/package.json index 13b7f45..273bc7e 100644 --- a/package.json +++ b/package.json @@ -12,17 +12,17 @@ "scripts": { "dev": "astro dev", "start": "astro dev", - "build": "astro check && astro build", + "build": "pnpm run astro:check && astro build", "preview": "astro preview", "astro": "astro", "new:post": "node scripts/new-post.js", - "astro:check": "astro check", + "astro:check": "astro sync && astro-check", "lint": "eslint .", "lint:fix": "eslint . --fix", "format": "prettier --write .", "format:check": "prettier --check .", - "check": "astro check && pnpm run lint && pnpm run format:check", - "check:fix": "astro check && pnpm run lint:fix && pnpm run format" + "check": "pnpm run astro:check && pnpm run lint && pnpm run format:check", + "check:fix": "pnpm run astro:check && pnpm run lint:fix && pnpm run format" }, "dependencies": { "@astrojs/rss": "^4.0.19", From f2f35b9c0c43b0b48142d95ae04905bccab8e584 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 04:59:18 +0000 Subject: [PATCH 2/2] fix(deploy): clean-install node_modules on Vercel Vercel restores node_modules from its build cache and ran a plain incremental `pnpm install`. pnpm records skipped optional packages in node_modules/.modules.yaml and keeps skipping them on later installs, even once a new lockfile makes them required. @emnapi/runtime@1.11.3 was optional (wasm32 bindings only) before Astro 7.4 and is a hard dependency of @astrojs/astro2tsx after it, so cached deploys came up without it and @astrojs/check could not load. `astro check` hid that by exiting 0; with the previous commit's astro-check the deploy fails instead, which is how this surfaced. Reproduced locally by installing the pre-7.4 lockfile, then incrementally installing this one: --frozen-lockfile alone doesn't repair it, `rm -rf node_modules` first does. (--force also works but installs every platform's optional binaries.) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011rvCtaiKiC4ZSvX9yewguJ --- CLAUDE.md | 6 ++++++ vercel.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 94b65e8..d6c1dca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -204,6 +204,12 @@ anything. Vercel picks it up. It deliberately does not deploy; a previous version tried to and produced a second, competing deploy path. +Vercel's `installCommand` deletes `node_modules` before a frozen install. Vercel +restores `node_modules` from its build cache, and pnpm keeps a record there of +optional packages it skipped; when a lockfile change makes one of those +required, an incremental install still skips it. That left `@emnapi/runtime` +missing after Astro 7.4, so `@astrojs/check` could not load. + If you touch `vercel.json`, remember this is a **static multi-page** site that emits real per-route HTML — not an SPA. It does not want a catch-all rewrite to `/index.html`. diff --git a/vercel.json b/vercel.json index b687993..b5fd4f9 100644 --- a/vercel.json +++ b/vercel.json @@ -2,7 +2,7 @@ "buildCommand": "pnpm run build", "outputDirectory": "dist", "framework": "astro", - "installCommand": "pnpm install", + "installCommand": "rm -rf node_modules && pnpm install --frozen-lockfile", "devCommand": "pnpm run dev", "redirects": [ {