Skip to content

Commit ba89e86

Browse files
authored
chore: expose CLAUDE.md and refine .gitignore for AI tool settings (#320)
1 parent c8b603a commit ba89e86

2 files changed

Lines changed: 217 additions & 3 deletions

File tree

.gitignore

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@ coverage
3535

3636
.DS_Store
3737

38-
# claude
39-
.claude
40-
CLAUDE.md
38+
# Claude Code - public files are committed (.claude/skills/, .claude/agents/, CLAUDE.md)
39+
# Private/personal files:
40+
.claude/settings.local.json
41+
.claude/memory/
42+
.claude/plans/
43+
.claude/skills/project-decisions/
44+
# Session notes (contain internal references)
45+
.claude/*.md
4146

4247
# context files (session notes, etc.)
4348
context/

CLAUDE.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# CLAUDE.md
2+
3+
## Project Overview
4+
5+
React utility hooks/components library. Monorepo with two packages:
6+
7+
- `react-simplikit` (`packages/core`) — Platform-independent React hooks & components
8+
- `@react-simplikit/mobile` (`packages/mobile`) — Mobile web utilities (viewport, keyboard, layout)
9+
10+
## Development Quick Start
11+
12+
```bash
13+
yarn build # Build all packages (tsup)
14+
yarn test # Run tests (Vitest)
15+
yarn fix # Auto-fix lint + format (ESLint + Prettier)
16+
yarn typecheck # Type check (tsc --noEmit) - alias: yarn run test:type
17+
yarn changeset # Create a changeset
18+
yarn changeset status # Check pending changesets
19+
```
20+
21+
## Architecture Rules
22+
23+
Layer dependency is **unidirectional** — no upward or circular imports allowed:
24+
25+
```
26+
components → hooks → utils → _internal
27+
```
28+
29+
- Components may use hooks, utils, \_internal
30+
- Hooks may use utils, \_internal
31+
- Utils may use \_internal only
32+
- \_internal has no internal dependencies
33+
- Mobile may depend on core; core must NOT depend on mobile
34+
35+
## File Structure Convention
36+
37+
Each hook/component/util lives in its own folder with co-located docs:
38+
39+
```
40+
src/
41+
├── hooks/
42+
│ └── useHookName/
43+
│ ├── index.ts # Re-export
44+
│ ├── useHookName.ts # Implementation
45+
│ ├── useHookName.spec.ts # Tests (core)
46+
│ ├── useHookName.test.ts # Tests (mobile)
47+
│ ├── useHookName.ssr.test.ts # SSR safety tests
48+
│ ├── useHookName.md # English docs
49+
│ └── ko/
50+
│ └── useHookName.md # Korean docs
51+
├── utils/
52+
│ └── utilName/
53+
│ ├── index.ts
54+
│ ├── utilName.ts
55+
│ └── utilName.test.ts
56+
└── index.ts # Public API exports (alphabetically sorted)
57+
```
58+
59+
## Coding Standards
60+
61+
- **`type` over `interface`** — Always use `type` for type aliases
62+
- **Named functions in useEffect** — Improves stack traces and readability
63+
```ts
64+
useEffect(function handleResize() { ... }, []); //
65+
useEffect(() => { ... }, []); //
66+
```
67+
- **Strict boolean checks** — Use explicit comparisons (`value !== undefined`, not `if (value)`)
68+
- **Import extensions** — Include `.js` in relative imports for ESM compliance
69+
- **useEffect cleanup** — Always return cleanup to remove listeners/subscriptions
70+
- **`"use client"` banner** — tsup adds this for RSC compatibility
71+
- **Named exports only** — No default exports
72+
- **No `any` types** — Full TypeScript strict mode, no escape hatches
73+
- **Zero dependencies** — No runtime dependencies in production code
74+
75+
### SSR-Safe Coding Pattern
76+
77+
All hooks/utils accessing browser APIs must be SSR-safe:
78+
79+
```ts
80+
// Pattern: Fixed initial value + useEffect sync
81+
const [state, setState] = useState(FIXED_INITIAL_VALUE);
82+
useEffect(function syncBrowserState() {
83+
if (isServer()) return;
84+
setState(getBrowserAPI());
85+
}, []);
86+
```
87+
88+
Never initialize state with browser API calls (causes hydration mismatch).
89+
90+
### Hook Return Value Convention
91+
92+
- **Single value**: `useDebounce<T>(value, delay): T`
93+
- **Tuple** (state + action, 2 items): `useToggle(init): [boolean, () => void]`
94+
- **Object** (3+ items): `usePagination(): { page, nextPage, prevPage }`
95+
96+
### Performance Patterns
97+
98+
- **Throttle** subscriptions at ~16ms (60fps) for viewport/keyboard events
99+
- **Deduplicate** to skip updates when value hasn't changed
100+
- **`startTransition`** for non-urgent state updates (React 18+)
101+
102+
## Testing
103+
104+
- **100% coverage mandatory** — Enforced by Vitest coverage threshold, no exceptions
105+
- **SSR tests required** — All hooks accessing browser APIs must have `.ssr.test.ts`
106+
- **Core tests**: `.spec.ts` (legacy, will migrate to `.test.ts`)
107+
- **Mobile tests**: `.test.ts`
108+
- **SSR pattern**:
109+
```ts
110+
import { renderHookSSR } from '../utils/renderHookSSR';
111+
it('is safe on server side rendering', () => {
112+
const result = renderHookSSR.serverOnly(() => useHookName());
113+
expect(result.current).toBeDefined();
114+
});
115+
```
116+
117+
## Documentation
118+
119+
- **Bilingual**: English + Korean (co-located in hook folders as `*.md` / `ko/*.md`)
120+
- **JSDoc required**: Every public API must have `@description` + `@example` + `@param` + `@returns`
121+
- **VitePress**: Used for documentation site; rewrites map source docs to clean URLs
122+
- **Co-location**: Docs live inside package source, not in separate docs/ tree
123+
- **Homepage**: `https://react-simplikit.slash.page`
124+
125+
## Commit Convention
126+
127+
Format: `<type>(<scope>): <description>`
128+
129+
- **Types**: `feat`, `fix`, `docs`, `chore`, `refactor`, `test`
130+
- **Scope**: Package name (`core`, `mobile`) or area
131+
- Examples: `feat(mobile): add useKeyboardHeight hook`, `fix: correct SSR rendering logic`
132+
133+
## PR Checklist
134+
135+
- [ ] Tests pass (`yarn test`)
136+
- [ ] Lint/format pass (`yarn fix`)
137+
- [ ] 100% coverage maintained
138+
- [ ] JSDoc with `@description` + `@example`
139+
- [ ] Changeset added (if user-facing change)
140+
141+
## Release Flow
142+
143+
Uses **Changesets** + **GitHub Actions OIDC** for automated releases.
144+
145+
```
146+
PR with changeset merged → release.yml triggers
147+
→ changesets/action detects changeset → creates "Version PR"
148+
→ Version PR merged → changesets/action: hasChangesets=false
149+
→ "Publish to npm" step → changeset publish (uses npm publish internally)
150+
```
151+
152+
### Critical: publishConfig does NOT work with npm
153+
154+
**npm does NOT support `publishConfig` overrides for manifest fields** (`main`, `types`, `module`, `exports`). Only `access`, `registry`, `tag` are supported. See [npm/cli#7586](https://github.com/npm/cli/issues/7586).
155+
156+
- `yarn npm publish` DOES support publishConfig field overrides
157+
- `changeset publish` internally calls `npm publish` (not yarn)
158+
- Therefore: **always declare `main`/`types`/`module`/`exports` at the top level** of package.json
159+
- `publishConfig` should only contain `access: "public"`
160+
161+
### OIDC Trusted Publishing
162+
163+
- npm auth uses GitHub Actions OIDC (no secret tokens needed)
164+
- Requires `id-token: write` permission in workflow
165+
- Node 22 ships npm v10 which doesn't support OIDC → `npm install -g npm@latest` upgrades to npm 11+
166+
- `changesets/action` must NOT have `publish` option (it overwrites `.npmrc` and breaks OIDC)
167+
- Publish is done in a separate step after changesets/action
168+
169+
### Snapshot/Canary Releases
170+
171+
```bash
172+
GITHUB_TOKEN=$(gh auth token) yarn changeset version --snapshot canary
173+
yarn changeset publish --tag canary # Requires npm login + OTP
174+
```
175+
176+
## Package Structure
177+
178+
```
179+
packages/
180+
├── core/ # react-simplikit
181+
│ ├── src/ # Source (hooks, components, utils)
182+
│ ├── dist/ # CJS build output
183+
│ ├── esm/ # ESM build output
184+
│ └── package.json
185+
└── mobile/ # @react-simplikit/mobile
186+
├── src/
187+
├── dist/ # CJS + ESM build output
188+
└── package.json
189+
```
190+
191+
## package.json Convention
192+
193+
```jsonc
194+
{
195+
"main": "./dist/index.cjs", // CJS entry (top level, NOT in publishConfig)
196+
"module": "./esm/index.js", // ESM entry for bundlers
197+
"types": "./dist/index.d.cts", // TypeScript types
198+
"exports": {
199+
// Modern Node.js/bundler resolution
200+
".": {
201+
"import": { "types": "...", "default": "..." },
202+
"require": { "types": "...", "default": "..." },
203+
},
204+
},
205+
"publishConfig": {
206+
"access": "public", // ONLY access here, nothing else
207+
},
208+
}
209+
```

0 commit comments

Comments
 (0)