specweave 1.0.239 → 1.0.241
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +31 -30
- package/README.md +1 -1
- package/bin/specweave.js +16 -0
- package/dist/plugins/specweave-ado/lib/ado-permission-gate.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-permission-gate.js +17 -2
- package/dist/plugins/specweave-ado/lib/ado-permission-gate.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +7 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +53 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.js +17 -2
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.js.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js +7 -3
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.d.ts.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js +27 -19
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-routing.d.ts +8 -0
- package/dist/plugins/specweave-testing/lib/playwright-routing.d.ts.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-routing.js +10 -7
- package/dist/plugins/specweave-testing/lib/playwright-routing.js.map +1 -1
- package/dist/src/adapters/agents-md-generator.js +1 -1
- package/dist/src/adapters/agents-md-generator.js.map +1 -1
- package/dist/src/adapters/claude/README.md +1 -1
- package/dist/src/adapters/claude-md-generator.js +1 -1
- package/dist/src/adapters/claude-md-generator.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +10 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/refresh-marketplace.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-marketplace.js +7 -67
- package/dist/src/cli/commands/refresh-marketplace.js.map +1 -1
- package/dist/src/cli/commands/team.d.ts +20 -0
- package/dist/src/cli/commands/team.d.ts.map +1 -0
- package/dist/src/cli/commands/team.js +101 -0
- package/dist/src/cli/commands/team.js.map +1 -0
- package/dist/src/cli/helpers/init/claude-settings-env.d.ts +16 -0
- package/dist/src/cli/helpers/init/claude-settings-env.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/claude-settings-env.js +44 -0
- package/dist/src/cli/helpers/init/claude-settings-env.js.map +1 -0
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.js +9 -13
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +12 -6
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.d.ts +2 -0
- package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
- package/dist/src/core/increment/discipline-checker.js +1 -1
- package/dist/src/core/increment/discipline-checker.js.map +1 -1
- package/dist/src/core/increment/status-commands.d.ts.map +1 -1
- package/dist/src/core/increment/status-commands.js +7 -0
- package/dist/src/core/increment/status-commands.js.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +2 -2
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.js +63 -25
- package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
- package/dist/src/core/reflection/reflect-handler.js +2 -2
- package/dist/src/core/reflection/reflect-handler.js.map +1 -1
- package/dist/src/core/session/handoff-context.js +2 -2
- package/dist/src/core/session/handoff-context.js.map +1 -1
- package/dist/src/sync/ado-reconciler.d.ts.map +1 -1
- package/dist/src/sync/ado-reconciler.js +21 -2
- package/dist/src/sync/ado-reconciler.js.map +1 -1
- package/dist/src/sync/engine.d.ts.map +1 -1
- package/dist/src/sync/engine.js +2 -0
- package/dist/src/sync/engine.js.map +1 -1
- package/dist/src/sync/github-reconciler.d.ts.map +1 -1
- package/dist/src/sync/github-reconciler.js +52 -26
- package/dist/src/sync/github-reconciler.js.map +1 -1
- package/dist/src/sync/jira-reconciler.d.ts.map +1 -1
- package/dist/src/sync/jira-reconciler.js +16 -3
- package/dist/src/sync/jira-reconciler.js.map +1 -1
- package/dist/src/sync/providers/ado.d.ts.map +1 -1
- package/dist/src/sync/providers/ado.js +4 -2
- package/dist/src/sync/providers/ado.js.map +1 -1
- package/dist/src/sync/providers/github.d.ts.map +1 -1
- package/dist/src/sync/providers/github.js +11 -0
- package/dist/src/sync/providers/github.js.map +1 -1
- package/dist/src/sync/providers/jira.d.ts.map +1 -1
- package/dist/src/sync/providers/jira.js +14 -2
- package/dist/src/sync/providers/jira.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +31 -6
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/auto-install.js +4 -4
- package/dist/src/utils/auto-install.js.map +1 -1
- package/package.json +2 -2
- package/plugins/FINAL-AUDIT-RECOMMENDATIONS.md +3 -3
- package/plugins/SKILLS-VS-AGENTS.md +1 -1
- package/plugins/specweave/PLUGIN.md +0 -2
- package/plugins/specweave/commands/export-skills.md +1 -1
- package/plugins/specweave/commands/role-orchestrator.md +1 -1
- package/plugins/specweave/hooks/log-decision.sh +6 -0
- package/plugins/specweave/hooks/stop-auto-v5.sh +17 -1
- package/plugins/specweave/hooks/stop-reflect.sh +16 -2
- package/plugins/specweave/hooks/stop-sync.sh +17 -9
- package/plugins/specweave/hooks/user-prompt-submit.sh +119 -35
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js +52 -26
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
- package/plugins/specweave/scripts/read-grill-context.sh +149 -0
- package/plugins/specweave/skills/code-review/SKILL.md +608 -0
- package/plugins/specweave/skills/done/SKILL.md +1 -1
- package/plugins/specweave/skills/grill/SKILL.md +91 -0
- package/plugins/specweave/skills/performance/SKILL.md +6 -0
- package/plugins/specweave/skills/security/SKILL.md +7 -0
- package/plugins/specweave/skills/security-patterns/SKILL.md +6 -0
- package/plugins/specweave/skills/tdd-orchestrator/SKILL.md +1 -1
- package/plugins/specweave/skills/team-build/SKILL.md +1 -1
- package/plugins/specweave/skills/team-orchestrate/SKILL.md +1 -1
- package/plugins/specweave/skills/tech-lead/SKILL.md +7 -0
- package/plugins/specweave-ado/lib/ado-permission-gate.js +18 -2
- package/plugins/specweave-ado/lib/ado-permission-gate.ts +19 -2
- package/plugins/specweave-frontend/skills/frontend/SKILL.md +138 -2
- package/plugins/specweave-frontend/skills/i18n-expert/SKILL.md +989 -0
- package/plugins/specweave-github/hooks/github-auto-create-handler.sh +23 -1
- package/plugins/specweave-github/lib/github-feature-sync.js +41 -0
- package/plugins/specweave-github/lib/github-feature-sync.ts +62 -0
- package/plugins/specweave-infrastructure/PLUGIN.md +2 -1
- package/plugins/specweave-infrastructure/skills/gcp-deep-dive/SKILL.md +1172 -0
- package/plugins/specweave-infrastructure/skills/observability/SKILL.md +6 -0
- package/plugins/specweave-infrastructure/skills/opentelemetry/SKILL.md +6 -0
- package/plugins/specweave-jira/lib/jira-permission-gate.js +18 -2
- package/plugins/specweave-jira/lib/jira-permission-gate.ts +19 -2
- package/plugins/specweave-mobile/PLUGIN.md +1 -2
- package/plugins/specweave-mobile/README.md +13 -12
- package/plugins/specweave-mobile/skills/capacitor-ionic/SKILL.md +4 -18
- package/plugins/specweave-mobile/skills/deep-linking-push/SKILL.md +4 -22
- package/plugins/specweave-mobile/skills/expo/SKILL.md +4 -24
- package/plugins/specweave-mobile/skills/mobile-testing/SKILL.md +4 -22
- package/plugins/specweave-mobile/skills/react-native-expert/SKILL.md +404 -47
- package/plugins/specweave-testing/PLUGIN.md +3 -11
- package/plugins/specweave-testing/lib/playwright-cli-detector.js +8 -3
- package/plugins/specweave-testing/lib/playwright-cli-detector.ts +8 -3
- package/plugins/specweave-testing/lib/playwright-cli-runner.js +25 -20
- package/plugins/specweave-testing/lib/playwright-cli-runner.ts +24 -19
- package/plugins/specweave-testing/lib/playwright-routing.js +1 -6
- package/plugins/specweave-testing/lib/playwright-routing.ts +11 -8
- package/plugins/specweave-testing/skills/accessibility-testing/SKILL.md +998 -0
- package/plugins/specweave-testing/skills/e2e-testing/SKILL.md +29 -28
- package/plugins/specweave-testing/skills/mutation-testing/SKILL.md +769 -0
- package/plugins/specweave-testing/skills/performance-testing/SKILL.md +961 -0
- package/plugins/specweave-testing/skills/qa-engineer/SKILL.md +2 -0
- package/plugins/specweave/.specweave/logs/decisions.jsonl +0 -12
- package/plugins/specweave/.specweave/logs/reflect/reflect.log +0 -8
- package/plugins/specweave/.specweave/logs/stop-auto.log +0 -6
- package/plugins/specweave/.specweave/logs/stop-sync.log +0 -10
- package/plugins/specweave/.specweave/state/dashboard.json +0 -43
- package/plugins/specweave/skills/infrastructure/SKILL.md +0 -86
- package/plugins/specweave/skills/qa-lead/SKILL.md +0 -77
- package/plugins/specweave-mobile/skills/mobile-architect/SKILL.md +0 -30
- package/plugins/specweave-testing/commands/e2e-setup.md +0 -1103
- package/plugins/specweave-testing/commands/test-coverage.md +0 -983
- package/plugins/specweave-testing/commands/test-generate.md +0 -1160
- package/plugins/specweave-testing/commands/test-init.md +0 -413
- package/plugins/specweave-testing/commands/ui-automate.md +0 -182
- package/plugins/specweave-testing/commands/ui-inspect.md +0 -82
|
@@ -1,17 +1,98 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: React Native and Expo expert
|
|
2
|
+
description: React Native and Expo architect and expert. Architecture, setup, Metro bundler, native modules, New Architecture (Fabric, Turbo Modules, JSI), Expo Router, offline-first, performance, debugging. Use for mobile app architecture, RN setup, build issues, or debugging.
|
|
3
3
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
4
4
|
model: opus
|
|
5
5
|
context: fork
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# React Native Expert - Setup, Build, Debug, Performance
|
|
8
|
+
# React Native Expert - Architecture, Setup, Build, Debug, Performance
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Elite mobile application architect and hands-on expert specializing in **React Native** and **Expo**. Covers the full spectrum from system design and scalable architecture to environment setup, Metro bundler, native modules, device testing, performance optimization, and debugging. Deep expertise in the New Architecture (Fabric, Turbo Modules, JSI) and modern React concurrent features.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Fetching Current Documentation
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**Before providing version-specific guidance, verify current versions.** Version numbers in static documentation become stale immediately. React Native releases every ~8 weeks. Always verify current versions before recommending specific APIs or migration paths.
|
|
15
|
+
|
|
16
|
+
For library documentation, use WebSearch or install Context7 manually: `claude plugin install context7@claude-plugins-official`
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Part 1: Architecture
|
|
21
|
+
|
|
22
|
+
### Architecture Principles
|
|
23
|
+
|
|
24
|
+
1. **Feature-based structure** over type-based (group by domain, not by file type)
|
|
25
|
+
2. **Unidirectional data flow** with clear state boundaries
|
|
26
|
+
3. **Platform abstraction** to isolate iOS/Android specifics
|
|
27
|
+
4. **Offline-first** for mobile reliability
|
|
28
|
+
5. **Lazy loading** screens and heavy modules for startup performance
|
|
29
|
+
|
|
30
|
+
### Recommended Project Structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
src/
|
|
34
|
+
├── app/ # Expo Router screens (file-based routing)
|
|
35
|
+
│ ├── _layout.tsx
|
|
36
|
+
│ ├── (tabs)/
|
|
37
|
+
│ ├── (auth)/
|
|
38
|
+
│ └── [...missing].tsx
|
|
39
|
+
├── features/ # Feature modules
|
|
40
|
+
│ ├── auth/
|
|
41
|
+
│ │ ├── components/
|
|
42
|
+
│ │ ├── hooks/
|
|
43
|
+
│ │ ├── services/
|
|
44
|
+
│ │ ├── stores/
|
|
45
|
+
│ │ └── types.ts
|
|
46
|
+
│ ├── feed/
|
|
47
|
+
│ └── profile/
|
|
48
|
+
├── shared/ # Shared utilities
|
|
49
|
+
│ ├── components/
|
|
50
|
+
│ ├── hooks/
|
|
51
|
+
│ ├── services/
|
|
52
|
+
│ └── utils/
|
|
53
|
+
├── providers/ # Context providers (theme, auth, query)
|
|
54
|
+
└── constants/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### State Management Selection Guide
|
|
58
|
+
|
|
59
|
+
| Solution | Use When | Persistence |
|
|
60
|
+
|----------|----------|-------------|
|
|
61
|
+
| Zustand | Most apps, simple global state | AsyncStorage via middleware |
|
|
62
|
+
| TanStack Query | Server state, caching, pagination | Built-in cache |
|
|
63
|
+
| Jotai | Fine-grained atomic state | AsyncStorage atoms |
|
|
64
|
+
| Legend State | High-frequency updates, real-time sync | Built-in persistence |
|
|
65
|
+
|
|
66
|
+
**Rule of thumb**: Use TanStack Query for server state + Zustand for client state. Avoid Redux unless team already uses it.
|
|
67
|
+
|
|
68
|
+
### Offline-First Architecture
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
┌─────────────┐ ┌──────────────┐ ┌─────────┐
|
|
72
|
+
│ UI Layer │────▶│ Data Layer │────▶│ API │
|
|
73
|
+
│ (React) │◀────│ (Cache + │◀────│ (REST/ │
|
|
74
|
+
│ │ │ Queue) │ │ GQL) │
|
|
75
|
+
└─────────────┘ └──────────────┘ └─────────┘
|
|
76
|
+
│
|
|
77
|
+
┌────▼────┐
|
|
78
|
+
│ SQLite │
|
|
79
|
+
│ or MMKV │
|
|
80
|
+
└─────────┘
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Key patterns:**
|
|
84
|
+
- Read from local cache first, sync in background
|
|
85
|
+
- Queue writes when offline, replay on reconnect
|
|
86
|
+
- Use optimistic updates for perceived performance
|
|
87
|
+
- Conflict resolution: last-write-wins or CRDTs
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Part 2: Setup and Environment
|
|
92
|
+
|
|
93
|
+
### Quick Setup Commands
|
|
94
|
+
|
|
95
|
+
#### Expo (Recommended)
|
|
15
96
|
|
|
16
97
|
```bash
|
|
17
98
|
# Create new project
|
|
@@ -24,7 +105,7 @@ npx expo install expo-dev-client
|
|
|
24
105
|
eas build --profile development --platform ios
|
|
25
106
|
```
|
|
26
107
|
|
|
27
|
-
|
|
108
|
+
#### React Native CLI
|
|
28
109
|
|
|
29
110
|
```bash
|
|
30
111
|
# Create project (New Architecture enabled by default)
|
|
@@ -39,18 +120,46 @@ npm run ios
|
|
|
39
120
|
npm run android
|
|
40
121
|
```
|
|
41
122
|
|
|
42
|
-
|
|
123
|
+
### Environment Requirements
|
|
43
124
|
|
|
44
125
|
| Component | Minimum | Recommended |
|
|
45
126
|
|-----------|---------|-------------|
|
|
46
127
|
| Node.js | 20.x | 22 LTS |
|
|
47
|
-
| React Native | 0.76+ |
|
|
48
|
-
| Expo SDK | 52+ |
|
|
49
|
-
| Xcode | 16.1 |
|
|
128
|
+
| React Native | 0.76+ | Latest stable |
|
|
129
|
+
| Expo SDK | 52+ | Latest stable |
|
|
130
|
+
| Xcode | 16.1 | Latest stable |
|
|
50
131
|
| Android SDK | 34 | 35 |
|
|
51
132
|
| CocoaPods | 1.14 | 1.15+ |
|
|
52
133
|
|
|
53
|
-
|
|
134
|
+
**Always verify current versions before advising.**
|
|
135
|
+
|
|
136
|
+
### Environment Variables
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# ~/.zshrc or ~/.bash_profile
|
|
140
|
+
export ANDROID_HOME=$HOME/Library/Android/sdk
|
|
141
|
+
export PATH=$PATH:$ANDROID_HOME/emulator
|
|
142
|
+
export PATH=$PATH:$ANDROID_HOME/platform-tools
|
|
143
|
+
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
|
|
144
|
+
|
|
145
|
+
# Reload
|
|
146
|
+
source ~/.zshrc
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Health Check
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
node --version # 20+
|
|
153
|
+
xcodebuild -version # 16.1+
|
|
154
|
+
pod --version # 1.15+
|
|
155
|
+
adb --version
|
|
156
|
+
watchman version
|
|
157
|
+
eas --version # Expo
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Part 3: Common Build Issues
|
|
54
163
|
|
|
55
164
|
### Metro Cache Issues
|
|
56
165
|
|
|
@@ -76,9 +185,33 @@ cd ios && rm -rf build Pods Podfile.lock && pod install && cd ..
|
|
|
76
185
|
cd android && ./gradlew clean && cd ..
|
|
77
186
|
```
|
|
78
187
|
|
|
79
|
-
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Part 4: New Architecture (Fabric, Turbo Modules, JSI)
|
|
80
191
|
|
|
81
|
-
###
|
|
192
|
+
### Fabric (New Rendering System)
|
|
193
|
+
|
|
194
|
+
Fabric replaces the old UIManager bridge with a synchronous, C++ rendering pipeline.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Fabric is enabled by default in Expo SDK 52+
|
|
198
|
+
// In app.json / app.config.ts:
|
|
199
|
+
{
|
|
200
|
+
"expo": {
|
|
201
|
+
"newArchEnabled": true
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Benefits of Fabric:**
|
|
207
|
+
- Synchronous layout calculations (no bridge delay)
|
|
208
|
+
- Concurrent rendering support (React 18 features)
|
|
209
|
+
- Shared C++ core between iOS and Android
|
|
210
|
+
- Better gesture handling and animations
|
|
211
|
+
|
|
212
|
+
### Turbo Modules
|
|
213
|
+
|
|
214
|
+
TurboModules replace the old Native Modules system with lazy-loaded, type-safe native interfaces.
|
|
82
215
|
|
|
83
216
|
```typescript
|
|
84
217
|
// specs/NativeCalculator.ts
|
|
@@ -87,11 +220,36 @@ import { TurboModuleRegistry } from 'react-native';
|
|
|
87
220
|
|
|
88
221
|
export interface Spec extends TurboModule {
|
|
89
222
|
add(a: number, b: number): number;
|
|
223
|
+
getDeviceModel(): string;
|
|
224
|
+
getBatteryLevel(): Promise<number>;
|
|
90
225
|
}
|
|
91
226
|
|
|
92
227
|
export default TurboModuleRegistry.getEnforcing<Spec>('NativeCalculator');
|
|
93
228
|
```
|
|
94
229
|
|
|
230
|
+
**Key advantages over old Native Modules:**
|
|
231
|
+
- Lazy initialization (loaded only when first accessed)
|
|
232
|
+
- Type-safe bridge via codegen
|
|
233
|
+
- Synchronous method calls possible via JSI
|
|
234
|
+
|
|
235
|
+
### JSI (JavaScript Interface)
|
|
236
|
+
|
|
237
|
+
JSI provides direct communication between JS and native code without JSON serialization.
|
|
238
|
+
|
|
239
|
+
```cpp
|
|
240
|
+
// C++ JSI host object example
|
|
241
|
+
class DeviceInfoHostObject : public jsi::HostObject {
|
|
242
|
+
public:
|
|
243
|
+
jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
|
|
244
|
+
auto propName = name.utf8(rt);
|
|
245
|
+
if (propName == "model") {
|
|
246
|
+
return jsi::String::createFromUtf8(rt, getDeviceModel());
|
|
247
|
+
}
|
|
248
|
+
return jsi::Value::undefined();
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
95
253
|
### Linking Native Code
|
|
96
254
|
|
|
97
255
|
```bash
|
|
@@ -100,7 +258,9 @@ cd ios && pod install
|
|
|
100
258
|
cd android && ./gradlew build
|
|
101
259
|
```
|
|
102
260
|
|
|
103
|
-
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Part 5: Performance Optimization
|
|
104
264
|
|
|
105
265
|
### Enable Hermes
|
|
106
266
|
|
|
@@ -108,7 +268,35 @@ cd android && ./gradlew build
|
|
|
108
268
|
// android/app/build.gradle
|
|
109
269
|
hermesEnabled = true
|
|
110
270
|
|
|
111
|
-
//
|
|
271
|
+
// iOS: Already default with New Architecture
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Optimized List Rendering
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { FlashList } from '@shopify/flash-list';
|
|
278
|
+
|
|
279
|
+
<FlashList
|
|
280
|
+
data={items}
|
|
281
|
+
renderItem={({ item }) => <ItemCard item={item} />}
|
|
282
|
+
estimatedItemSize={80}
|
|
283
|
+
keyExtractor={(item) => item.id}
|
|
284
|
+
/>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Image Optimization
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Expo Image (recommended)
|
|
291
|
+
import { Image } from 'expo-image';
|
|
292
|
+
|
|
293
|
+
<Image
|
|
294
|
+
source={{ uri: imageUrl }}
|
|
295
|
+
placeholder={{ blurhash: 'LGF5]+Yk^6#M@-5c,1J5@[or[Q6.' }}
|
|
296
|
+
contentFit="cover"
|
|
297
|
+
transition={200}
|
|
298
|
+
cachePolicy="memory-disk"
|
|
299
|
+
/>
|
|
112
300
|
```
|
|
113
301
|
|
|
114
302
|
### Performance Monitoring
|
|
@@ -123,19 +311,28 @@ new PerformanceObserver((list) => {
|
|
|
123
311
|
}).observe({ entryTypes: ['measure'] });
|
|
124
312
|
```
|
|
125
313
|
|
|
126
|
-
###
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
// Use FastImage for better caching
|
|
130
|
-
import FastImage from 'react-native-fast-image';
|
|
314
|
+
### Bundle Size Analysis
|
|
131
315
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
316
|
+
```bash
|
|
317
|
+
# Analyze bundle with expo-atlas
|
|
318
|
+
EXPO_ATLAS=1 npx expo export --platform ios
|
|
319
|
+
npx expo-atlas path/to/atlas-file
|
|
136
320
|
```
|
|
137
321
|
|
|
138
|
-
|
|
322
|
+
### Performance Checklist
|
|
323
|
+
|
|
324
|
+
- [ ] Use FlashList over FlatList for long lists
|
|
325
|
+
- [ ] Memoize expensive computations with useMemo/React.memo
|
|
326
|
+
- [ ] Use expo-image instead of Image for caching and blurhash
|
|
327
|
+
- [ ] Profile with React DevTools Profiler
|
|
328
|
+
- [ ] Enable Hermes engine
|
|
329
|
+
- [ ] Avoid inline object/function creation in render
|
|
330
|
+
- [ ] Use useCallback for event handlers passed to children
|
|
331
|
+
- [ ] Lazy-load heavy screens with React.lazy + Suspense
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Part 6: Device Testing
|
|
139
336
|
|
|
140
337
|
### iOS Simulator
|
|
141
338
|
|
|
@@ -163,13 +360,15 @@ emulator -avd Pixel_8_API_35
|
|
|
163
360
|
npx react-native run-android --deviceId <device-id>
|
|
164
361
|
```
|
|
165
362
|
|
|
166
|
-
|
|
363
|
+
---
|
|
167
364
|
|
|
168
|
-
|
|
365
|
+
## Part 7: Debugging
|
|
366
|
+
|
|
367
|
+
### Chrome DevTools / New Debugger
|
|
169
368
|
|
|
170
369
|
```bash
|
|
171
|
-
#
|
|
172
|
-
#
|
|
370
|
+
# RN 0.73+: Press 'j' in Metro to open debugger
|
|
371
|
+
# Replaces deprecated Flipper
|
|
173
372
|
```
|
|
174
373
|
|
|
175
374
|
### React DevTools
|
|
@@ -193,30 +392,188 @@ npx react-native start --reset-cache
|
|
|
193
392
|
cd android && ./gradlew clean && cd ..
|
|
194
393
|
```
|
|
195
394
|
|
|
196
|
-
|
|
395
|
+
### Network Debugging
|
|
197
396
|
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
397
|
+
```typescript
|
|
398
|
+
// In development, use React Native Debugger or Flipper Network plugin
|
|
399
|
+
// For production, use a logging service (Sentry, LogRocket)
|
|
400
|
+
|
|
401
|
+
// Quick debug proxy
|
|
402
|
+
if (__DEV__) {
|
|
403
|
+
// XMLHttpRequest interceptor for logging
|
|
404
|
+
const originalXHROpen = XMLHttpRequest.prototype.open;
|
|
405
|
+
XMLHttpRequest.prototype.open = function(...args) {
|
|
406
|
+
console.log('XHR:', args[0], args[1]);
|
|
407
|
+
return originalXHROpen.apply(this, args);
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Part 8: Navigation with Expo Router
|
|
415
|
+
|
|
416
|
+
### Directory Structure
|
|
204
417
|
|
|
205
|
-
|
|
206
|
-
|
|
418
|
+
```
|
|
419
|
+
app/
|
|
420
|
+
_layout.tsx # Root layout (providers, auth)
|
|
421
|
+
index.tsx # Home screen (/)
|
|
422
|
+
(tabs)/
|
|
423
|
+
_layout.tsx # Tab navigator
|
|
424
|
+
home.tsx # /home tab
|
|
425
|
+
profile.tsx # /profile tab
|
|
426
|
+
(auth)/
|
|
427
|
+
_layout.tsx # Auth group layout
|
|
428
|
+
login.tsx # /login
|
|
429
|
+
register.tsx # /register
|
|
430
|
+
settings/
|
|
431
|
+
_layout.tsx # Stack navigator for settings
|
|
432
|
+
index.tsx # /settings
|
|
433
|
+
[id].tsx # /settings/:id (dynamic route)
|
|
434
|
+
[...missing].tsx # 404 catch-all
|
|
207
435
|
```
|
|
208
436
|
|
|
209
|
-
|
|
437
|
+
### Root Layout with Providers
|
|
210
438
|
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
439
|
+
```typescript
|
|
440
|
+
// app/_layout.tsx
|
|
441
|
+
import { Stack } from 'expo-router';
|
|
442
|
+
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
|
|
443
|
+
import { ThemeProvider } from '../providers/ThemeProvider';
|
|
444
|
+
import { AuthProvider } from '../providers/AuthProvider';
|
|
445
|
+
|
|
446
|
+
const queryClient = new QueryClient();
|
|
447
|
+
|
|
448
|
+
export default function RootLayout() {
|
|
449
|
+
return (
|
|
450
|
+
<QueryClientProvider client={queryClient}>
|
|
451
|
+
<AuthProvider>
|
|
452
|
+
<ThemeProvider>
|
|
453
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
454
|
+
<Stack.Screen name="(tabs)" />
|
|
455
|
+
<Stack.Screen name="(auth)" />
|
|
456
|
+
<Stack.Screen name="settings" options={{ presentation: 'modal' }} />
|
|
457
|
+
</Stack>
|
|
458
|
+
</ThemeProvider>
|
|
459
|
+
</AuthProvider>
|
|
460
|
+
</QueryClientProvider>
|
|
461
|
+
);
|
|
462
|
+
}
|
|
218
463
|
```
|
|
219
464
|
|
|
465
|
+
### Navigation Patterns
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { router, useLocalSearchParams, Link } from 'expo-router';
|
|
469
|
+
|
|
470
|
+
// Imperative navigation
|
|
471
|
+
router.push('/settings/123');
|
|
472
|
+
router.replace('/home');
|
|
473
|
+
router.back();
|
|
474
|
+
router.navigate('/profile');
|
|
475
|
+
|
|
476
|
+
// Typed params
|
|
477
|
+
const { id } = useLocalSearchParams<{ id: string }>();
|
|
478
|
+
|
|
479
|
+
// Declarative link
|
|
480
|
+
<Link href="/settings/456" asChild>
|
|
481
|
+
<Pressable><Text>Go to Settings</Text></Pressable>
|
|
482
|
+
</Link>
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Part 9: State Management Patterns
|
|
488
|
+
|
|
489
|
+
### Zustand (Recommended for Most Apps)
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
import { create } from 'zustand';
|
|
493
|
+
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
494
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
495
|
+
|
|
496
|
+
interface AuthStore {
|
|
497
|
+
token: string | null;
|
|
498
|
+
user: User | null;
|
|
499
|
+
login: (token: string, user: User) => void;
|
|
500
|
+
logout: () => void;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export const useAuthStore = create<AuthStore>()(
|
|
504
|
+
persist(
|
|
505
|
+
(set) => ({
|
|
506
|
+
token: null,
|
|
507
|
+
user: null,
|
|
508
|
+
login: (token, user) => set({ token, user }),
|
|
509
|
+
logout: () => set({ token: null, user: null }),
|
|
510
|
+
}),
|
|
511
|
+
{
|
|
512
|
+
name: 'auth-storage',
|
|
513
|
+
storage: createJSONStorage(() => AsyncStorage),
|
|
514
|
+
}
|
|
515
|
+
)
|
|
516
|
+
);
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Jotai (Atomic State)
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
import { atom, useAtom } from 'jotai';
|
|
523
|
+
import { atomWithStorage, createJSONStorage } from 'jotai/utils';
|
|
524
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
525
|
+
|
|
526
|
+
const storage = createJSONStorage<string>(() => AsyncStorage);
|
|
527
|
+
const themeAtom = atomWithStorage<'light' | 'dark'>('theme', 'light', storage);
|
|
528
|
+
|
|
529
|
+
const userAtom = atom<User | null>(null);
|
|
530
|
+
const isLoggedInAtom = atom((get) => get(userAtom) !== null);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Part 10: Testing
|
|
536
|
+
|
|
537
|
+
### Unit and Component Testing
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// Jest + React Native Testing Library
|
|
541
|
+
import { render, fireEvent, waitFor } from '@testing-library/react-native';
|
|
542
|
+
import { LoginScreen } from '../screens/LoginScreen';
|
|
543
|
+
|
|
544
|
+
describe('LoginScreen', () => {
|
|
545
|
+
it('shows error for invalid email', async () => {
|
|
546
|
+
const { getByPlaceholderText, getByText } = render(<LoginScreen />);
|
|
547
|
+
fireEvent.changeText(getByPlaceholderText('Email'), 'invalid');
|
|
548
|
+
fireEvent.press(getByText('Login'));
|
|
549
|
+
await waitFor(() => {
|
|
550
|
+
expect(getByText('Invalid email address')).toBeTruthy();
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### E2E Testing with Detox
|
|
557
|
+
|
|
558
|
+
```javascript
|
|
559
|
+
// e2e/login.test.ts
|
|
560
|
+
describe('Login Flow', () => {
|
|
561
|
+
beforeAll(async () => { await device.launchApp(); });
|
|
562
|
+
beforeEach(async () => { await device.reloadReactNative(); });
|
|
563
|
+
|
|
564
|
+
it('should login successfully', async () => {
|
|
565
|
+
await element(by.id('email-input')).typeText('user@example.com');
|
|
566
|
+
await element(by.id('password-input')).typeText('password123');
|
|
567
|
+
await element(by.id('login-button')).tap();
|
|
568
|
+
await expect(element(by.id('home-screen'))).toBeVisible();
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
220
575
|
## Related Skills
|
|
221
576
|
|
|
222
|
-
- `
|
|
577
|
+
- `expo` - Expo managed workflow, EAS Build/Submit, OTA updates, config plugins
|
|
578
|
+
- `mobile-testing` - Comprehensive testing strategies (XCTest, Espresso, Detox, Maestro)
|
|
579
|
+
- `deep-linking-push` - Deep linking and push notification details
|
|
@@ -15,17 +15,9 @@ Comprehensive testing infrastructure expert covering unit testing (Vitest/Jest),
|
|
|
15
15
|
| qa-engineer | Expert QA engineer for test strategy and automation across unit, integration, E2E, accessibility, and performance testing |
|
|
16
16
|
| e2e-testing | End-to-end testing expert for Playwright, Cypress, visual regression (Percy, Chromatic), and UI automation with debugging capabilities |
|
|
17
17
|
| unit-testing | Unit testing and TDD expert for Vitest/Jest with red-green-refactor workflow, mocking strategies, coverage analysis, and ESM mocking patterns |
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
| Command | Description |
|
|
22
|
-
|---------|-------------|
|
|
23
|
-
| /sw-testing:test-init | Initialize comprehensive testing infrastructure with Vitest, Playwright, and testing best practices |
|
|
24
|
-
| /sw-testing:test-generate | Generate test files from source code with coverage analysis and test templates |
|
|
25
|
-
| /sw-testing:test-coverage | Analyze test coverage and identify gaps with coverage thresholds |
|
|
26
|
-
| /sw-testing:e2e-setup | Set up end-to-end testing with Playwright or Cypress configuration |
|
|
27
|
-
| /sw-testing:ui-automate | Automate UI testing with visual regression and accessibility checks |
|
|
28
|
-
| /sw-testing:ui-inspect | Inspect UI elements and generate test locators for robust selectors |
|
|
18
|
+
| mutation-testing | Mutation testing expert using Stryker Mutator for TypeScript/JavaScript — setup, score analysis, survived mutant triage, and CI integration |
|
|
19
|
+
| performance-testing | Performance testing expert for k6, Artillery, Lighthouse CI, Core Web Vitals, and database performance benchmarking |
|
|
20
|
+
| accessibility-testing | Accessibility testing expert for axe-core, pa11y, WCAG compliance, screen reader testing, and keyboard navigation |
|
|
29
21
|
|
|
30
22
|
## Installation
|
|
31
23
|
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
2
|
let cachedResult = null;
|
|
3
|
+
function clearCache() {
|
|
4
|
+
cachedResult = null;
|
|
5
|
+
}
|
|
3
6
|
function detectPlaywrightCli(options = {}) {
|
|
4
7
|
const { useCache = false } = options;
|
|
5
8
|
if (useCache && cachedResult) {
|
|
6
9
|
return cachedResult;
|
|
7
10
|
}
|
|
11
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
8
12
|
let path;
|
|
9
13
|
let version;
|
|
10
14
|
try {
|
|
11
|
-
path =
|
|
15
|
+
path = execFileSync(whichCmd, ["playwright-cli"], {
|
|
12
16
|
encoding: "utf-8",
|
|
13
17
|
timeout: 5e3
|
|
14
18
|
}).trim();
|
|
@@ -18,7 +22,7 @@ function detectPlaywrightCli(options = {}) {
|
|
|
18
22
|
return result2;
|
|
19
23
|
}
|
|
20
24
|
try {
|
|
21
|
-
version =
|
|
25
|
+
version = execFileSync("playwright-cli", ["--version"], {
|
|
22
26
|
encoding: "utf-8",
|
|
23
27
|
timeout: 5e3
|
|
24
28
|
}).trim();
|
|
@@ -29,5 +33,6 @@ function detectPlaywrightCli(options = {}) {
|
|
|
29
33
|
return result;
|
|
30
34
|
}
|
|
31
35
|
export {
|
|
36
|
+
clearCache,
|
|
32
37
|
detectPlaywrightCli
|
|
33
38
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
2
|
|
|
3
3
|
export interface CliDetectionResult {
|
|
4
4
|
installed: boolean;
|
|
@@ -12,6 +12,10 @@ export interface DetectOptions {
|
|
|
12
12
|
|
|
13
13
|
let cachedResult: CliDetectionResult | null = null;
|
|
14
14
|
|
|
15
|
+
export function clearCache(): void {
|
|
16
|
+
cachedResult = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionResult {
|
|
16
20
|
const { useCache = false } = options;
|
|
17
21
|
|
|
@@ -19,11 +23,12 @@ export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionRe
|
|
|
19
23
|
return cachedResult;
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
22
27
|
let path: string | undefined;
|
|
23
28
|
let version: string | undefined;
|
|
24
29
|
|
|
25
30
|
try {
|
|
26
|
-
path =
|
|
31
|
+
path = execFileSync(whichCmd, ['playwright-cli'], {
|
|
27
32
|
encoding: 'utf-8',
|
|
28
33
|
timeout: 5_000,
|
|
29
34
|
}).trim();
|
|
@@ -34,7 +39,7 @@ export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionRe
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
try {
|
|
37
|
-
version =
|
|
42
|
+
version = execFileSync('playwright-cli', ['--version'], {
|
|
38
43
|
encoding: 'utf-8',
|
|
39
44
|
timeout: 5_000,
|
|
40
45
|
}).trim();
|