跳到正文
DEV Community· Chris F A·· 3 小時前AI 評分60

Claude Code + Expo:改善 React Native 程式碼生成的倉庫設定

Claude Code + Expo: The Repo Config That Makes It Write Decent React Native

AI 導讀

作者整理 Expo/React Native 專案的 Claude Code 倉庫設定,涵蓋專案規則、命令權限、編輯後檢查及可重用 Skills。示例以 Expo SDK 54、TypeScript、Expo Router、NativeWind 和 Supabase 為技術組合,並透過 ESLint 和 tsc 把錯誤回傳給 Claude。

正文

TL;DR

  • Claude Code forgets your project every session. Write the facts down in CLAUDE.md: stack, commands, and do/don't rules like "use npx expo install".
  • Allowlist routine commands in .claude/settings.json, put risky ones behind ask or deny.
  • A PostToolUse hook runs ESLint and tsc after every edit and feeds errors back to Claude with exit 2.
  • Skills (.claude/skills/<name>/SKILL.md) turn "add a screen" into a repeatable /add-screen command.
  • Still read the diff and run it on a phone.

CLAUDE.md: keep it short and factual

CLAUDE.md in the repo root loads into every session. /init will draft one; then cut it down. The docs suggest staying under 200 lines. Here's a trimmed version for an Expo app:

## Stack
- Expo SDK 54, TypeScript (strict), Expo Router, NativeWind, Supabase

## Commands
- Dev: `npx expo start`
- Typecheck: `npx tsc --noEmit`
- Lint: `npx expo lint`
- Dep check: `npx expo install --check`

## Rules
- Add packages with `npx expo install <pkg>`, never npm/yarn add.
- Every file in app/ is a route. Components go in components/.
- Style with NativeWind className, not StyleSheet.create.
- Navigate with expo-router `Link` / `router`.
- Never put secrets in EXPO_PUBLIC_ vars. They're inlined into the bundle.
- Don't hand-edit ios/ or android/.

Why these lines:

  • npx expo install picks versions that match your SDK. Plain npm install doesn't.
  • Expo Router treats anything in the routes folder as a route, so a stray utils.ts in app/ becomes a broken screen. (Newer Expo docs use src/app/. Write what your repo uses.)
  • Expo's own docs say EXPO_PUBLIC_ values end up in plain text in your compiled app.

Got an AGENTS.md already? Put @AGENTS.md at the top of CLAUDE.md to import it instead of duplicating.

For rules that only matter in one folder, use .claude/rules/ with a paths field. They only load when Claude touches matching files:

---
paths:
  - "supabase/**/*"
---
- New tables get RLS enabled in the same migration.
- Never edit an applied migration.

Permissions: stop clicking "yes" forty times

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npx expo install *)",
      "Bash(npx expo lint *)",
      "Bash(npx tsc --noEmit *)",
      "Bash(npx eslint *)"
    ],
    "ask": ["Bash(git push *)", "Bash(npx expo prebuild *)", "Bash(eas *)"],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git reset --hard *)",
      "Read(./.env)",
      "Read(./.env.*)"
    ]
  }
}

Quick gotchas:

  • Rules are checked deny, then ask, then allow. Deny always wins.
  • Bash(npx tsc --noEmit *) also matches the bare npx tsc --noEmit.
  • cmd && other gets split, so an allow on cmd doesn't sneak other through.
  • I skip Bash(npx expo *). It would approve prebuild --clean too.
  • Bash deny rules don't catch /bin/rm or bash -c. They prevent accidents, they're not a sandbox.

The hook that does the most work

CLAUDE.md is a suggestion. Hooks always run. Add this to the same settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-ts.sh" }
        ]
      }
    ]
  }
}

.claude/hooks/check-ts.sh (needs jq, and chmod +x):

#!/usr/bin/env bash
FILE=$(jq -r '.tool_input.file_path // empty')
case "$FILE" in *.ts|*.tsx) ;; *) exit 0 ;; esac
cd "$CLAUDE_PROJECT_DIR" || exit 0
errors=""
out=$(npx eslint "$FILE" 2>&1) || errors+="$out"$'\n'
out=$(npx tsc --noEmit --pretty false 2>&1) || errors+="$out"
if [ -n "$errors" ]; then echo "$errors" >&2; exit 2; fi

For PostToolUse, exit 2 doesn't undo the edit. It sends stderr to Claude, so it sees the type errors and fixes them right away. Check it registered with /hooks. If whole-project tsc gets slow, move it to a Stop hook.

Commands and skills

Custom commands were merged into skills. .claude/commands/check.md and .claude/skills/check/SKILL.md both give you /check. I use skills for anything multi-step:

---
name: add-screen
description: Add an Expo Router screen with NativeWind styling and navigation
argument-hint: [route-path] [what it does]
disable-model-invocation: true
allowed-tools: Bash(npx tsc --noEmit *) Bash(npx expo lint *)
---

Add a screen at app/$0.tsx. Purpose: $ARGUMENTS

1. Read app/_layout.tsx and a nearby screen to match patterns.
2. For dynamic segments like [id], use useLocalSearchParams with typed params.
3. Reusable UI goes in components/. NativeWind className only.
4. Link to it from the screen that should open it.
5. Run typecheck and lint, fix what you introduced, list changed files.
  • disable-model-invocation: true means only you can trigger it.
  • allowed-tools pre-approves those commands while it runs.
  • $0 is the first argument, $ARGUMENTS is everything.

Then:

/add-screen orders/[id] "order detail with status and a cancel button"

For bigger changes, ask for a plan first. Plan mode is where you catch "I'll just add React Navigation" before it touches your files.

Don't want to write all this?

Fair. I work on AppLighter: Expo SDK 54 templates with TypeScript, Expo Router, NativeWind and Supabase (RLS on every table), shipped with CLAUDE.md, AGENTS.md, .cursorrules, and skills/commands like /add-screen, /swap-backend, /audit-security and /ship. The files above work fine on their own too.

Review before you trust it

  • git diff everything. Look for out-of-scope files, new deps, non-route files in app/.
  • npx expo install --check for SDK version mismatches.
  • Grep new EXPO_PUBLIC_ vars. Assume they're public, because they are.
  • Run it on a real phone. Back nav, keyboard, safe areas. Typecheck can't see those.

If Claude gets the same thing wrong twice, that's a missing line in CLAUDE.md.

What's in your React Native CLAUDE.md that I didn't cover? Drop your best rule in the comments.

來源:DEV Community · dev.to