Skip to content

Commit 9553d7e

Browse files
elicwhitemeta-codesync[bot]
authored andcommitted
Replace CONTRIBUTING.md with AGENTS.md (#55301)
Summary: Pull Request resolved: #55301 Replace the brief CONTRIBUTING.md with a comprehensive AGENTS.md that provides AI coding assistants with detailed guidance for working with the react-native-compatibility-check package. The new documentation covers the three-stage pipeline architecture, compatibility rules, testing patterns, and design principles that were previously only partially documented. Changelog: [Internal] Reviewed By: makovkastar Differential Revision: D91275922 fbshipit-source-id: 46fa6d887dd62f4f650d3a77468eb3e864275802
1 parent 89b8628 commit 9553d7e

2 files changed

Lines changed: 178 additions & 36 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# AGENTS.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Overview
6+
7+
This package is a **type checker for React Native's JS/Native boundary**. It detects backwards-incompatible changes between JavaScript and Native code to prevent crashes, particularly useful for:
8+
- Local development (detecting when native rebuild is needed)
9+
- Over-the-air (OTA) updates
10+
- Server Components with React Native
11+
12+
The tool operates on JSON schema files generated by `@react-native/codegen`, making it agnostic to TypeScript/Flow.
13+
14+
## Architecture: Three-Stage Pipeline
15+
16+
The compatibility check flows through three distinct stages:
17+
18+
```
19+
Schema (new) ──┐
20+
├──▶ TypeDiffing ──▶ VersionDiffing ──▶ ErrorFormatting ──▶ Output
21+
Schema (old) ──┘
22+
```
23+
24+
### Stage 1: TypeDiffing (`TypeDiffing.js`)
25+
**Pure type comparison** - Compares two type annotations and returns all structural differences.
26+
- Reports ALL differences between types (added/removed properties, union changes, etc.)
27+
- Returns `ComparisonResult` with status: `matching`, `skipped`, `properties`, `members`, `unionMembers`, `functionChange`, `positionalTypeChange`, `nullableChange`, or `error`
28+
- **Must remain pure** - no React Native-specific logic belongs here
29+
30+
### Stage 2: VersionDiffing (`VersionDiffing.js`)
31+
**Semantic safety analysis** - Interprets TypeDiffing results in the context of React Native's boundary.
32+
- Determines if changes are safe based on **data flow direction**:
33+
- `toNative`: Data flows from JS to Native (method parameters, component props)
34+
- `fromNative`: Data flows from Native to JS (return values, getConstants)
35+
- `both`: Bidirectional flow
36+
- Encodes compatibility rules:
37+
- Adding to a union sent TO native = **UNSAFE** (native won't expect it)
38+
- Removing from a union received FROM native = **UNSAFE** (JS won't handle it)
39+
- Adding optional properties = **SAFE**
40+
- Making required properties optional when sending TO native = **UNSAFE**
41+
42+
### Stage 3: ErrorFormatting (`ErrorFormatting.js`)
43+
**Human-readable output** - Converts deep error objects into formatted strings.
44+
- **Must remain pure** - no business logic
45+
46+
### Supporting Files
47+
48+
- **`ComparisonResult.js`**: Type definitions for all comparison result shapes
49+
- **`DiffResults.js`**: Type definitions for schema diff results, error codes, and summary types
50+
- **`SortTypeAnnotations.js`**: Sorting utilities for comparing type annotations in a stable order
51+
- **`convertPropToBasicTypes.js`**: Converts Component prop types to standard type annotations for comparison
52+
- **`index.js`**: Public API - exports `compareSchemas()` returning a `CompatCheckResult`
53+
54+
## Key Type Definitions
55+
56+
```javascript
57+
// Main comparison statuses
58+
type ComparisonResult =
59+
| {status: 'matching'} // Types are identical
60+
| {status: 'skipped'} // No old type to compare
61+
| {status: 'properties', ...} // Object property changes
62+
| {status: 'members', ...} // Enum member changes
63+
| {status: 'unionMembers', ...} // Union member changes
64+
| {status: 'functionChange', ...}// Function signature changes
65+
| {status: 'error', ...} // Incompatible type change
66+
67+
// Summary statuses
68+
type DiffSummary = {
69+
status: 'ok' | 'patchable' | 'incompatible',
70+
incompatibilityReport: {...}
71+
}
72+
```
73+
74+
## Commands
75+
76+
Run tests from the react-native-compatibility-check directory:
77+
```bash
78+
cd packages/react-native-compatibility-check
79+
80+
# Run all tests
81+
yarn test
82+
83+
# Run a specific test file
84+
yarn test src/__tests__/TypeDiffing-test.js
85+
86+
# Run tests matching a pattern
87+
yarn test --testNamePattern="compareTypes on unions"
88+
```
89+
90+
**Meta employees**: Use `js1 test SUBPATH` instead (e.g., `js1 test react-native-compatibility-check`).
91+
92+
## Testing Patterns
93+
94+
### Test Fixtures
95+
Tests use Flow files in `__tests__/__fixtures__/` parsed by `@react-native/codegen`:
96+
- **Native Modules**: `native-module-*/NativeModule.js.flow`
97+
- **Native Components**: `native-component-*/NativeComponent.js.flow`
98+
99+
The `getTestSchema()` utility parses these fixtures into schema objects.
100+
101+
### Test Structure
102+
- **TypeDiffing-test.js**: Tests pure type comparison logic
103+
- **VersionDiffing-test.js**: Tests safety analysis with boundary direction
104+
- **ErrorFormatting-test.js**: Tests error message generation (uses snapshots)
105+
106+
### Adding Test Cases
107+
1. Create a new fixture directory under `__tests__/__fixtures__/`
108+
2. Add a `.js.flow` file defining a Native Module or Component
109+
3. Load it in tests using `getTestSchema(__dirname, '__fixtures__', 'fixture-name', 'FileName.js.flow')`
110+
111+
## Design Principles
112+
113+
### Separation of Concerns
114+
- **TypeDiffing**: Pure type comparison. Should work for ANY JavaScript types.
115+
- **VersionDiffing**: React Native boundary semantics. Only place for RN-specific logic.
116+
- **ErrorFormatting**: Presentation only. No business logic.
117+
118+
### Module-scope Type Registries
119+
`TypeDiffing.js` uses module-scope variables (`_newerTypesReg`, `_olderTypesReg`, `_newerEnumMap`, `_olderEnumMap`) to avoid threading lookups through all recursive calls. This is acceptable because the logic is serial.
120+
121+
### Structural Type Comparison
122+
Types are compared structurally, not nominally. Two different type aliases with identical structure are considered matching.
123+
124+
## Compatibility Rules Reference
125+
126+
### Data Flowing TO Native (parameters, props)
127+
| Change | Safe? |
128+
|--------|-------|
129+
| Add optional property ||
130+
| Add required property ||
131+
| Remove property ||
132+
| Make property optional ||
133+
| Add union member ||
134+
| Remove union member ||
135+
| Add enum member ||
136+
| Remove enum member ||
137+
138+
### Data Flowing FROM Native (return values, constants)
139+
| Change | Safe? |
140+
|--------|-------|
141+
| Add optional property ||
142+
| Add required property ||
143+
| Remove property ||
144+
| Make property required ||
145+
| Add union member ||
146+
| Remove union member ||
147+
| Add enum member ||
148+
| Remove enum member ||
149+
150+
## Common Gotchas
151+
152+
1. **Component Commands**: Adding/removing commands is intentionally allowed even though it could cause OTA issues, because there's no feature detection mechanism for commands.
153+
154+
2. **Union ordering**: Unions are sorted before comparison, so `'a' | 'b'` equals `'b' | 'a'`.
155+
156+
3. **Nullable vs Optional**: These are distinct concepts:
157+
- Optional: Property may be absent (`prop?: T`)
158+
- Nullable: Value may be null/undefined (`prop: ?T`)
159+
160+
4. **Type Aliases**: Resolved during comparison. Different alias names with identical structure are treated as matching.
161+
162+
5. **Component Props with Defaults**: `WithDefault` types are stripped during comparison - only the underlying type matters for compatibility.
163+
164+
6. **Int32EnumTypeAnnotation**: Currently converted to `AnyTypeAnnotation` because the tool lacks support for number literal unions.
165+
166+
## Adding New Type Support
167+
168+
1. Add the type case to `compareTypeAnnotation()` in `TypeDiffing.js`
169+
2. Add sorting logic in `SortTypeAnnotations.js` (`compareTypeAnnotationForSorting`)
170+
3. Add formatting in `ErrorFormatting.js` (`formatTypeAnnotation`)
171+
4. Add test fixtures and tests covering the new type
172+
5. If it affects safety analysis, update `VersionDiffing.js` checks
173+
174+
## Code Style
175+
176+
- All source files use `@flow strict-local` or `@flow strict`
177+
- All source files require `@format` pragma for Prettier
178+
- Tests use `@noflow` or `@flow` (not strict)

packages/react-native-compatibility-check/CONTRIBUTING.md

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)