Skip to content

Commit e22bf8f

Browse files
authored
feat: brownfield navigation skills (#303)
* feat: add brownfield navigation skills * feat: update brownfield navigation skills * fix: review comments * refactor: rearrange skills
1 parent 11bdb1f commit e22bf8f

4 files changed

Lines changed: 382 additions & 0 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
name: brownfield-navigation
3+
description: Allows presenting existing native screens from React Native. Define the schema in TypeScript, run codegen to generate the bindings, invoke the function on the JS side, generate the XCFramework/AAR, and integrate native bindings such as the delegate into the host app.
4+
license: MIT
5+
metadata:
6+
author: Callstack
7+
tags: react-native, expo, brownfield, navigation
8+
---
9+
10+
# Overview
11+
12+
Brownfield Navigation flows from a TypeScript contract (`brownfield.navigation.ts`) through `npx brownfield navigation:codegen`, which generates JS bindings, native stubs, and delegate protocols. Host apps implement `BrownfieldNavigationDelegate`, register it with `BrownfieldNavigationManager` at startup, then React Native code calls the generated `@callstack/brownfield-navigation` module.
13+
14+
# When to Apply
15+
16+
Reference these skills when:
17+
- App uses brownfield setup
18+
- Presenting existing native screen from React Native
19+
- Configuring schema for brownfield navigation
20+
- Implementing the brownfield navigation delegate
21+
22+
# Quick Reference
23+
24+
- Generate the files using codegen script:
25+
```bash
26+
npx brownfield navigation:codegen
27+
```
28+
- Brownfield packaging commands also run the same navigation codegen as part of packaging workflows:
29+
```bash
30+
# iOS
31+
npx brownfield package:ios
32+
33+
# android
34+
npx brownfield package:android
35+
npx brownfield publish:android
36+
```
37+
38+
## Routing (concern → file)
39+
40+
Concern | Read
41+
--------|------
42+
JS call sites, `BrownfieldNavigation.*` usage, `undefined is not a function`, params vs generated API | [`javascript-usage.md`](references/javascript-usage.md)
43+
`BrownfieldNavigationDelegate`, `setDelegate` / `shared.setDelegate`, startup crashes, no-op / wrong native route | [`native-integration.md`](references/native-integration.md)
44+
Contract placement, `BrownfieldNavigationSpec` / `Spec`, `navigation:codegen`, generated artifacts, stale or missing outputs | [`setup-codegen.md`](references/setup-codegen.md)
45+
46+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Brownfield Navigation JavaScript Usage
2+
3+
## Discoverability triggers
4+
5+
- "how to call BrownfieldNavigation from JS"
6+
- "`undefined is not a function` on a Brownfield method"
7+
- "JS method missing after updating `brownfield.navigation.ts`"
8+
- "Brownfield JS method exists but opens wrong destination"
9+
10+
## Scope
11+
12+
In scope:
13+
- Calling `BrownfieldNavigation.<method>()` from React Native code.
14+
- JS call-site patterns for buttons/screens and parameter passing.
15+
- Runtime troubleshooting for JS-facing failures (`undefined is not a function`, missing methods, API drift signals).
16+
- Reminding users when contract changes require codegen and native rebuild.
17+
18+
Out of scope:
19+
- Authoring `brownfield.navigation.ts` and codegen mechanics. For that, read [`setup-codegen.md`](setup-codegen.md) in this folder.
20+
- Android/iOS delegate implementation and startup registration details. For that, read [`native-integration.md`](native-integration.md) in this folder.
21+
22+
## Procedure
23+
24+
1. Confirm readiness before discussing JS calls
25+
- Native delegate registration must already be in place before JS uses the module.
26+
- If registration/startup order is uncertain, read [`native-integration.md`](native-integration.md) in this folder.
27+
28+
2. Provide the default JS invocation pattern
29+
- Import `BrownfieldNavigation` from `@callstack/brownfield-navigation`.
30+
- Call generated methods directly from handlers (for example, button `onPress`).
31+
- Keep method names and argument shape aligned with the generated API.
32+
33+
3. Recommend call-site best practices
34+
- Pass stable explicit params (`userId`, IDs, flags), not transient UI-derived data.
35+
- Keep each JS method call mapped to a clearly named native destination.
36+
- Use simple direct calls first; avoid wrapping in unnecessary abstractions while debugging.
37+
38+
4. Apply troubleshooting flow for runtime failures
39+
- `undefined is not a function`: method changed in spec but codegen/rebuild not reapplied.
40+
- Native crash on call: likely delegate registration/startup ordering issue; hand off implementation details to [`native-integration.md`](native-integration.md) in this folder.
41+
- Method exists but wrong route/no-op: generated method present, but native delegate wiring likely incorrect; route delegate fixes to [`native-integration.md`](native-integration.md) in this folder.
42+
43+
5. Enforce regeneration rule when JS/native API drift appears
44+
- If method names, params, or return types changed in `brownfield.navigation.ts`, rerun:
45+
`npx brownfield navigation:codegen`
46+
- Then rebuild native apps before retesting JS calls.
47+
48+
6. Route non-JS root causes quickly
49+
- Spec placement/signature/codegen output questions → [`setup-codegen.md`](setup-codegen.md) in this folder.
50+
- Delegate implementation/registration/lifecycle questions → [`native-integration.md`](native-integration.md) in this folder.
51+
52+
## Minimal TSX example
53+
54+
Assume the generated contract includes a method like `openNativeProfile(userId: string): void`. The exact method names and params come from the generated `@callstack/brownfield-navigation` module after running codegen.
55+
56+
```tsx
57+
import React from 'react';
58+
import {Button, SafeAreaView} from 'react-native';
59+
import BrownfieldNavigation from '@callstack/brownfield-navigation';
60+
61+
export function ProfileLauncherScreen(): React.JSX.Element {
62+
return (
63+
<SafeAreaView>
64+
<Button
65+
title="Open native profile"
66+
onPress={() => {
67+
BrownfieldNavigation.openNativeProfile('user-123');
68+
}}
69+
/>
70+
</SafeAreaView>
71+
);
72+
}
73+
```
74+
75+
Portable takeaways:
76+
- Import the generated module from `@callstack/brownfield-navigation`.
77+
- Call the generated method directly from a user action such as `onPress`.
78+
- If this method is missing or throws `undefined is not a function`, treat it as a codegen/rebuild drift signal first.
79+
80+
## Quick reference
81+
82+
- Import: `import BrownfieldNavigation from '@callstack/brownfield-navigation'`
83+
- Typical calls:
84+
- `BrownfieldNavigation.navigateToSettings()`
85+
- `BrownfieldNavigation.navigateToReferrals('user-123')`
86+
- Regenerate on contract change: `npx brownfield navigation:codegen`
87+
- Retest order:
88+
1. Confirm contract shape
89+
2. Regenerate
90+
3. Rebuild native apps
91+
4. Retest JS call sites
92+
- Error cues this skill should handle first:
93+
- `undefined is not a function` on a Brownfield method
94+
- JS method missing after updating `brownfield.navigation.ts`
95+
- JS call parameters appear out of sync with generated API
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Brownfield Navigation Native Integration
2+
3+
## Discoverability triggers
4+
5+
- "implement `BrownfieldNavigationDelegate`"
6+
- "where to call `BrownfieldNavigationManager.setDelegate(...)`"
7+
- "where to call `BrownfieldNavigationManager.shared.setDelegate(...)`"
8+
- "navigation call crashes at app startup or upon invocation"
9+
10+
## Scope
11+
12+
In scope:
13+
- Implementing generated `BrownfieldNavigationDelegate` methods in Android and iOS host code.
14+
- Registering the delegate with `BrownfieldNavigationManager` during app startup.
15+
- Enforcing startup/lifecycle ordering (delegate registered before JS calls).
16+
- Troubleshooting native wiring issues (crash/no-op/wrong route).
17+
18+
Out of scope:
19+
- Authoring `brownfield.navigation.ts` and running codegen. For that, read [`setup-codegen.md`](setup-codegen.md) in this folder.
20+
- JavaScript call-site usage patterns in RN screens. For that, read [`javascript-usage.md`](javascript-usage.md) in this folder.
21+
22+
## Procedure
23+
24+
1. Confirm prerequisites
25+
- `BrownfieldNavigation.xcframework` is linked in the iOS host app.
26+
- If applicable, use the artifact produced by `npx brownfield package:ios` in the current project. The exact output path can vary by package manager and workspace layout.
27+
- `Gson` dependency is added to the Android host app.
28+
29+
2. Implement Android delegate
30+
- Implement generated `BrownfieldNavigationDelegate` in the host `Activity` (or class with navigation context).
31+
- Wire each generated method to the native destination and map params exactly.
32+
- Typical implementation starts Android `Activity` instances with `Intent` extras.
33+
34+
3. Register Android delegate during startup
35+
- Call `BrownfieldNavigationManager.setDelegate(...)` in startup flow (for example `onCreate`).
36+
- Registration must happen before any React Native screen can call `BrownfieldNavigation.*`.
37+
38+
4. Implement iOS delegate
39+
- Create a class conforming to `BrownfieldNavigationDelegate`.
40+
- Wire each generated method to the intended native presentation flow (UIKit/SwiftUI).
41+
- Ensure screen presentation runs on the main/UI thread.
42+
43+
5. Register iOS delegate during startup
44+
- Call `BrownfieldNavigationManager.shared.setDelegate(navigationDelegate: ...)` at app startup (for example in app `init`).
45+
- Registration must happen before RN-rendered routes can invoke module methods.
46+
47+
6. Enforce lifecycle requirements
48+
- Delegate must be present before JS usage; missing delegate is a startup bug.
49+
- Re-register if the host object owning the delegate is recreated.
50+
- Keep navigation/presentation work on main thread.
51+
52+
7. Triage runtime integration failures
53+
- Method added/changed in TS but missing natively: rerun `npx brownfield navigation:codegen` and rebuild.
54+
- Crash on launch or first method call: verify delegate registration order vs RN route rendering.
55+
- Method exists but wrong destination/no-op: verify delegate implementation wiring and parameter mapping.
56+
57+
## Minimal native examples
58+
59+
Assume the generated contract includes a method like `openNativeProfile(userId: string): void`. The generated delegate method name and parameter types come from the current project's `brownfield.navigation.ts`.
60+
61+
### Kotlin example
62+
63+
Use this pattern when the host screen or activity owns Android navigation context:
64+
65+
```kotlin
66+
import android.content.Intent
67+
import android.os.Bundle
68+
import androidx.appcompat.app.AppCompatActivity
69+
import com.callstack.nativebrownfieldnavigation.BrownfieldNavigationDelegate
70+
import com.callstack.nativebrownfieldnavigation.BrownfieldNavigationManager
71+
72+
class MainActivity : AppCompatActivity(), BrownfieldNavigationDelegate {
73+
override fun onCreate(savedInstanceState: Bundle?) {
74+
super.onCreate(savedInstanceState)
75+
BrownfieldNavigationManager.setDelegate(this)
76+
}
77+
78+
override fun openNativeProfile(userId: String) {
79+
val intent = Intent(this, ProfileActivity::class.java).apply {
80+
putExtra("userId", userId)
81+
}
82+
startActivity(intent)
83+
}
84+
}
85+
```
86+
87+
Portable takeaways:
88+
- Implement the generated `BrownfieldNavigationDelegate`.
89+
- Register the delegate before any React Native code can call `BrownfieldNavigation.*`.
90+
- Map each generated method to an explicit native destination and pass params through unchanged.
91+
92+
### Swift example
93+
94+
Use this pattern when an app-level object can own navigation setup and present UIKit or SwiftUI flows:
95+
96+
```swift
97+
import BrownfieldNavigation
98+
import UIKit
99+
100+
final class AppNavigationDelegate: BrownfieldNavigationDelegate {
101+
func openNativeProfile(userId: String) {
102+
DispatchQueue.main.async {
103+
guard let rootViewController = UIApplication.shared.connectedScenes
104+
.compactMap({ $0 as? UIWindowScene })
105+
.flatMap(\.windows)
106+
.first(where: \.isKeyWindow)?
107+
.rootViewController else {
108+
return
109+
}
110+
111+
let viewController = ProfileViewController(userId: userId)
112+
rootViewController.present(viewController, animated: true)
113+
}
114+
}
115+
}
116+
117+
final class AppDelegate: UIResponder, UIApplicationDelegate {
118+
private let navigationDelegate = AppNavigationDelegate()
119+
120+
func application(
121+
_ application: UIApplication,
122+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
123+
) -> Bool {
124+
BrownfieldNavigationManager.shared.setDelegate(navigationDelegate: navigationDelegate)
125+
return true
126+
}
127+
}
128+
```
129+
130+
Portable takeaways:
131+
- Keep a strong reference to the delegate for as long as React Native might call it.
132+
- Register the delegate during startup, before RN screens that use Brownfield navigation are shown.
133+
- Perform presentation on the main thread and keep the implementation focused on routing plus param mapping.
134+
135+
## Quick reference
136+
137+
- Android delegate type: `BrownfieldNavigationDelegate`
138+
- Android registration: `BrownfieldNavigationManager.setDelegate(...)`
139+
- iOS delegate type: `BrownfieldNavigationDelegate`
140+
- iOS registration: `BrownfieldNavigationManager.shared.setDelegate(navigationDelegate: ...)`
141+
- Integration order:
142+
1. Generate/update contract outputs
143+
2. Implement delegate methods natively
144+
3. Register delegate at startup
145+
4. Render RN routes that call `BrownfieldNavigation.*`
146+
- Error cues this skill should address:
147+
- Crashes when JS calls navigation methods early
148+
- Missing delegate registration at startup
149+
- Wrong native screen opened from a JS call
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Brownfield Navigation Setup and Codegen
2+
3+
## Discoverability triggers
4+
5+
- "where to put `brownfield.navigation.ts`"
6+
- "run brownfield navigation codegen"
7+
- "generated files missing after codegen"
8+
- "spec changes not reflected in generated APIs"
9+
10+
## Scope
11+
12+
In scope:
13+
- Authoring and updating `brownfield.navigation.ts` in the React Native app root.
14+
- Enforcing supported signature rules (`BrownfieldNavigationSpec`/`Spec`, typed params, optional params, `void`-first guidance).
15+
- Running `npx brownfield navigation:codegen`.
16+
- Explaining generated artifacts and when regeneration + native rebuild are required.
17+
- First-pass setup/codegen triage (missing files, stale API after spec changes).
18+
19+
Out of scope:
20+
- Android/iOS delegate implementation and registration details. For that, read [`native-integration.md`](native-integration.md) in this folder.
21+
- JavaScript screen usage patterns and runtime call ergonomics. For that, read [`javascript-usage.md`](javascript-usage.md) in this folder.
22+
23+
## Procedure
24+
25+
1. Confirm prerequisites
26+
- `@callstack/brownfield-navigation` is installed. Otherwise, install the latest version
27+
- Babel deps used by codegen are available (`@babel/core`, `@react-native/babel-preset`). Otherwise, install the compatible version OR ask the user.
28+
29+
2. Verify contract file placement and shape
30+
- File name is exactly `brownfield.navigation.ts`.
31+
- File is in the React Native app root.
32+
- Interface is `BrownfieldNavigationSpec` (or `Spec`).
33+
34+
3. Validate method signatures before codegen
35+
- Method names are valid TypeScript identifiers.
36+
- Typed params and optional params are allowed.
37+
- Prefer synchronous `void` navigation methods.
38+
- Warn that Promise-based methods are not currently supported by generated native implementations on iOS/Android; they may compile but reject with `not_implemented`.
39+
40+
4. Choose the right codegen invocation
41+
- Default form: `npx brownfield navigation:codegen`
42+
- Explicit path form: `npx brownfield navigation:codegen <specPath>`
43+
- Use the default form when your current working directory is the React Native app root and the contract file is the default `./brownfield.navigation.ts`.
44+
- Use the explicit-path form when you are running from another directory, when the app lives inside a workspace/monorepo, or when you want to remove ambiguity about which spec file should be parsed.
45+
- Relative `specPath` values are resolved from the directory where you run the command; absolute paths also work.
46+
47+
5. Understand the artifact root before verifying outputs
48+
- Codegen writes into the installed `@callstack/brownfield-navigation` package root, not into the app directory that contains `brownfield.navigation.ts`.
49+
- The exact absolute location depends on the consumer's package manager and workspace layout. It may live under a local `node_modules` tree, a hoisted workspace dependency, or another package-store-managed install location.
50+
- Treat the package root as the stable anchor, then verify these generated relative paths beneath it:
51+
- `src/NativeBrownfieldNavigation.ts`
52+
- `src/index.ts`
53+
- `lib/commonjs/index.js`
54+
- `lib/module/index.js`
55+
- `lib/typescript/commonjs/src/index.d.ts`
56+
- `lib/typescript/module/src/index.d.ts`
57+
- `ios/BrownfieldNavigationDelegate.swift`
58+
- `ios/BrownfieldNavigationModels.swift` when complex model types are generated
59+
- `ios/NativeBrownfieldNavigation.mm`
60+
- Android files under `android/src/main/java/<generated-package>/`, including `BrownfieldNavigationDelegate.kt`, `NativeBrownfieldNavigationModule.kt`, and `BrownfieldNavigationModels.kt` when complex model types are generated
61+
62+
6. Enforce rerun/rebuild rule
63+
- Any change that affects the contract surface in `brownfield.navigation.ts` requires rerunning codegen, then rebuilding native apps.
64+
- This includes adding, removing, renaming, or retyping methods; changing params or optionality; and introducing/removing model types used by params.
65+
- If JavaScript can no longer see a generated method, or native code still behaves like the old contract, assume regeneration or rebuild was skipped.
66+
- Safe order:
67+
1) update the contract
68+
2) rerun codegen
69+
3) rebuild iOS and/or Android before retesting
70+
71+
7. Handoff when issue is outside setup/codegen
72+
- Delegate lifecycle/startup order: [`native-integration.md`](native-integration.md) in this folder.
73+
- JS invocation/runtime call-site guidance: [`javascript-usage.md`](javascript-usage.md) in this folder.
74+
75+
## Quick reference
76+
77+
- Primary commands:
78+
- `npx brownfield navigation:codegen`
79+
- `npx brownfield navigation:codegen <specPath>`
80+
- Contract file: `brownfield.navigation.ts` at React Native app root
81+
- Default command behavior: reads `./brownfield.navigation.ts` from the current working directory
82+
- Output location: generated files are written under the resolved `@callstack/brownfield-navigation` package root
83+
- Parser-supported interface names: `BrownfieldNavigationSpec` or `Spec`
84+
- Safe default return type: `void`
85+
- Important order:
86+
1. Define/update TS contract
87+
2. Run codegen
88+
3. Rebuild native apps
89+
- Error cues this skill should address:
90+
- Generated files missing or stale after spec edits
91+
- JS/native surfaces not reflecting renamed/added methods
92+
- Promise-based navigation method behaving as `not_implemented`

0 commit comments

Comments
 (0)