Skip to content

Commit 5c96ee8

Browse files
committed
feat: redesign starter with bob, strict TS, full tests, and AI-ready tooling
BREAKING CHANGE: lib/ renamed to src/; build output moved to lib/ via bob.
1 parent ea31c90 commit 5c96ee8

29 files changed

+2138
-223
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
---
2+
description: React Native library conventions for this starter. Apply when creating or editing components, hooks, types, or tests in src/.
3+
globs: ["src/**/*.ts", "src/**/*.tsx"]
4+
alwaysApply: true
5+
---
6+
7+
# Library Conventions
8+
9+
## Component Structure
10+
11+
Every component lives in its own folder under `src/components/`:
12+
13+
```
14+
src/components/MyComponent/
15+
├── MyComponent.tsx # Implementation
16+
├── MyComponent.types.ts # Props interface
17+
└── index.ts # Barrel: re-exports default + named + types
18+
```
19+
20+
### Props Interface Pattern
21+
22+
```tsx
23+
// MyComponent.types.ts
24+
import type { StyleProp, ViewStyle } from "react-native";
25+
26+
/**
27+
* Props for the {@link MyComponent} component.
28+
*/
29+
export interface MyComponentProps {
30+
/** Required title — always document every prop with TSDoc. */
31+
title: string;
32+
33+
/** Optional. Always mark optional props with `?`. */
34+
style?: StyleProp<ViewStyle>;
35+
36+
/** @defaultValue false */
37+
enabled?: boolean;
38+
}
39+
```
40+
41+
### Component Implementation Pattern
42+
43+
```tsx
44+
// MyComponent.tsx
45+
import React from "react";
46+
import { StyleSheet, View } from "react-native";
47+
import type { MyComponentProps } from "./MyComponent.types";
48+
49+
/**
50+
* TSDoc summary line.
51+
*
52+
* @example
53+
* ```tsx
54+
* <MyComponent title="Hello" />
55+
* ```
56+
*/
57+
const MyComponent: React.FC<MyComponentProps> = ({
58+
title,
59+
style,
60+
enabled = false,
61+
testID = "my-component",
62+
}) => {
63+
return (
64+
<View style={[styles.container, style]} testID={testID}>
65+
{/* implementation */}
66+
</View>
67+
);
68+
};
69+
70+
const styles = StyleSheet.create({
71+
container: {},
72+
});
73+
74+
export default MyComponent;
75+
```
76+
77+
### Barrel Pattern
78+
79+
```ts
80+
// index.ts
81+
export { default } from "./MyComponent";
82+
export { default as MyComponent } from "./MyComponent";
83+
export type { MyComponentProps } from "./MyComponent.types";
84+
```
85+
86+
## Hook Pattern
87+
88+
```ts
89+
// src/hooks/useMyHook.ts
90+
export interface UseMyHookOptions {
91+
/** TSDoc every option. */
92+
initialValue?: number;
93+
}
94+
95+
export interface UseMyHookReturn {
96+
/** TSDoc every returned value. */
97+
value: number;
98+
setValue: (v: number) => void;
99+
}
100+
101+
/**
102+
* @example
103+
* const { value, setValue } = useMyHook({ initialValue: 0 });
104+
*/
105+
export function useMyHook(options: UseMyHookOptions = {}): UseMyHookReturn {
106+
// implementation
107+
}
108+
```
109+
110+
- Named export only (no default export for hooks).
111+
- Export `Options` and `Return` interfaces.
112+
113+
## Public API (`src/index.ts`)
114+
115+
- Export every public value AND its types.
116+
- Never export internal helpers.
117+
118+
```ts
119+
export { MyComponent } from "./components/MyComponent";
120+
export type { MyComponentProps } from "./components/MyComponent";
121+
export { useMyHook } from "./hooks/useMyHook";
122+
export type { UseMyHookOptions, UseMyHookReturn } from "./hooks/useMyHook";
123+
```
124+
125+
## TypeScript Rules
126+
127+
- `import type` for type-only imports.
128+
- No `any` — use `unknown` or proper generics.
129+
- No `ts-ignore` without an explanatory comment.
130+
- Props defaults should be destructured with `= value` syntax.
131+
132+
## Styling Rules
133+
134+
- Always use `StyleSheet.create()`.
135+
- No inline style objects (`style={{ margin: 8 }}` in JSX is off for production code).
136+
- Style overrides accepted as `StyleProp<ViewStyle | TextStyle | ImageStyle>`.
137+
138+
## testID Convention
139+
140+
- Accept `testID?: string` with a sensible default (e.g. `"my-component"`).
141+
- Derive child testIDs: `${testID}-title`, `${testID}-button`, etc.
142+
143+
## Testing Rules
144+
145+
- Tests live in `src/__tests__/` or `src/components/MyComponent/__tests__/`.
146+
- Use `@testing-library/react-native`.
147+
- Group with `describe` blocks: `rendering`, `interactions`, `accessibility`.
148+
- Hook tests use `renderHook` + `act`.
149+
- Never use `snapshot` tests for this library.
150+
151+
## File Naming
152+
153+
| Type | Convention | Example |
154+
|---|---|---|
155+
| Component | PascalCase | `MyButton.tsx` |
156+
| Component types | PascalCase + `.types` | `MyButton.types.ts` |
157+
| Hook | camelCase with `use` prefix | `useDebounce.ts` |
158+
| Test | same name + `.test` | `MyButton.test.tsx` |
159+
| Type utilities | camelCase | `index.ts` |
160+
161+
## Commit Messages (Conventional Commits)
162+
163+
```
164+
feat: add MyButton component
165+
fix: correct accessibility role on MyComponent
166+
test: add useMyHook boundary tests
167+
docs: add TSDoc examples to MyComponent props
168+
```

.eslintignore

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
node_modules/**
2-
vendor/**
3-
build/**
4-
dist/**
2+
lib/**
3+
coverage/**
54
*.min.js
65
*.bundle.js
76
*.snap
87
*.log
98
*.lock
109
*.map
11-
*.test.js
12-
*.test.jsx
13-
*.test.ts
14-
*.test.tsx
15-
*.spec.js
16-
*.spec.jsx
17-
*.spec.ts
18-
*.spec.tsx
19-
example/vendor/**
20-
example/build/**
21-
example/dist/**

.eslintrc.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = {
1717
extends: [
1818
"eslint:recommended",
1919
"plugin:react/recommended",
20+
"plugin:react-hooks/recommended",
2021
"plugin:@typescript-eslint/recommended",
2122
"plugin:prettier/recommended",
2223
],
@@ -64,9 +65,9 @@ module.exports = {
6465
"*.js",
6566
"*.svg",
6667
"*.json",
67-
"*.png",
6868
"package.json",
6969
"package-lock.json",
70+
"lib/**",
7071
],
7172
rules: {
7273
"prettier/prettier": [
@@ -84,18 +85,33 @@ module.exports = {
8485
],
8586
"max-len": ["error", 120],
8687
"no-useless-catch": "off",
87-
"react-hooks/exhaustive-deps": "off",
88+
"react-hooks/exhaustive-deps": "warn",
8889
"@typescript-eslint/ban-ts-comment": "warn",
8990
"@typescript-eslint/no-empty-function": "off",
9091
"@typescript-eslint/no-explicit-any": "warn",
9192
"@typescript-eslint/explicit-module-boundary-types": "off",
9293
"@typescript-eslint/no-empty-interface": "off",
94+
"@typescript-eslint/consistent-type-imports": [
95+
"error",
96+
{ prefer: "type-imports" },
97+
],
9398
"react/jsx-filename-extension": ["error", { extensions: [".tsx"] }],
99+
"react/react-in-jsx-scope": "off",
94100
"react-native/no-unused-styles": "error",
95101
"react-native/split-platform-components": "error",
96102
"react-native/no-inline-styles": "off",
97103
"react-native/no-color-literals": "off",
98104
"react-native/no-raw-text": "off",
105+
"unused-imports/no-unused-imports": "error",
106+
"unused-imports/no-unused-vars": [
107+
"warn",
108+
{
109+
vars: "all",
110+
varsIgnorePattern: "^_",
111+
args: "after-used",
112+
argsIgnorePattern: "^_",
113+
},
114+
],
99115
"import/extensions": [
100116
"error",
101117
"never",
@@ -106,7 +122,6 @@ module.exports = {
106122
png: "always",
107123
jpg: "always",
108124
json: "always",
109-
constant: "always",
110125
},
111126
],
112127
"import/no-extraneous-dependencies": "error",

.github/workflows/ci.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
pull_request:
9+
branches:
10+
- main
11+
- master
12+
13+
jobs:
14+
typecheck:
15+
name: Typecheck
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: "lts/*"
25+
cache: "npm"
26+
27+
- name: Install dependencies
28+
run: npm ci
29+
30+
- name: Run typecheck
31+
run: npm run typecheck
32+
33+
lint:
34+
name: Lint
35+
runs-on: ubuntu-latest
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
40+
- name: Setup Node.js
41+
uses: actions/setup-node@v4
42+
with:
43+
node-version: "lts/*"
44+
cache: "npm"
45+
46+
- name: Install dependencies
47+
run: npm ci
48+
49+
- name: Run ESLint
50+
run: npm run lint:ci
51+
52+
- name: Run Prettier check
53+
run: npm run prettier:ci
54+
55+
test:
56+
name: Test
57+
runs-on: ubuntu-latest
58+
steps:
59+
- name: Checkout
60+
uses: actions/checkout@v4
61+
62+
- name: Setup Node.js
63+
uses: actions/setup-node@v4
64+
with:
65+
node-version: "lts/*"
66+
cache: "npm"
67+
68+
- name: Install dependencies
69+
run: npm ci
70+
71+
- name: Run tests with coverage
72+
run: npm run test:coverage
73+
74+
- name: Upload coverage report
75+
uses: actions/upload-artifact@v4
76+
if: always()
77+
with:
78+
name: coverage-report
79+
path: coverage/
80+
retention-days: 7
81+
82+
build:
83+
name: Build
84+
runs-on: ubuntu-latest
85+
needs: [typecheck, lint, test]
86+
steps:
87+
- name: Checkout
88+
uses: actions/checkout@v4
89+
90+
- name: Setup Node.js
91+
uses: actions/setup-node@v4
92+
with:
93+
node-version: "lts/*"
94+
cache: "npm"
95+
96+
- name: Install dependencies
97+
run: npm ci
98+
99+
- name: Build library
100+
run: npm run build
101+
102+
- name: Upload build artifacts
103+
uses: actions/upload-artifact@v4
104+
with:
105+
name: lib
106+
path: lib/
107+
retention-days: 7

0 commit comments

Comments
 (0)