Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/nine-mice-fall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@callstack/react-native-brownfield': patch
'@callstack/brownfield-cli': patch
---

feat: support brownfield unified config file in Expo
5 changes: 5 additions & 0 deletions .changeset/young-snails-lose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@callstack/brownfield-cli': patch
---

fix: generate config schema with descriptions, add more descriptions
2 changes: 1 addition & 1 deletion apps/AppleApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"devDependencies": {
"@callstack/brownfield-example-shared-tests": "workspace:^",
"@rock-js/tools": "^0.13.3",
"@rock-js/tools": "^0.13.5",
"detox": "^20.27.0",
"jest": "^29.7.0"
}
Expand Down
7 changes: 1 addition & 6 deletions apps/ExpoApp54/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,7 @@
}
}
],
[
"@callstack/react-native-brownfield",
{
"debug": true
}
],
"@callstack/react-native-brownfield",
"expo-web-browser"
],
"experiments": {
Expand Down
100 changes: 93 additions & 7 deletions docs/docs/docs/api-reference/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,65 @@ If the CLI finds multiple sources, it throws an error instead of guessing which
When both a config value and a CLI flag are set for the same option, the CLI flag wins.
The CLI also validates the file against the published schema and logs warnings for unknown or invalid keys.

## Expo projects

For Expo apps, the Brownfield config file is also used by the `@callstack/react-native-brownfield` Expo config plugin during `expo prebuild`.

Use **one configuration source** for Brownfield options:

- `brownfield.config.js`, `brownfield.config.json`, or `package.json#brownfield`
- **or** plugin options in the `app.json` `plugins` tuple

Do not define the same options in both places. If a Brownfield config file exists and `app.json` also passes plugin options, prebuild throws an error.

### Shared keys between CLI and Expo plugin

| Config file key | Expo plugin equivalent | Notes |
| -------------------- | ---------------------- | ---------------------------------------------------------- |
| `android.moduleName` | `android.moduleName` | Same key for packaging and prebuild scaffolding. |
| `ios.scheme` | `ios.frameworkName` | Same value; `ios.scheme` is the canonical config file key. |
| `verbose` | `debug` | Same intent; `verbose` is the canonical config file key. |

### Expo-only keys

Plugin-only options that do not map to CLI flags belong under nested `expo` objects:

- `android.expo.*` for Android prebuild scaffolding
- `ios.expo.*` for iOS prebuild scaffolding

Example:

```json
{
"$schema": "https://oss.callstack.com/react-native-brownfield/schema.json",
"verbose": true,
"android": {
"moduleName": "brownfieldlib",
"variant": "release",
"expo": {
"packageName": "com.example.app",
"minSdkVersion": 24
}
},
"ios": {
"scheme": "BrownfieldLib",
"configuration": "Release",
"expo": {
"bundleIdentifier": "com.example.app.brownfield",
"deploymentTarget": "15.0"
}
}
}
```

For Expo projects, register the plugin without options when using a config file:

```json
{
"plugins": ["@callstack/react-native-brownfield"]
}
```

## JavaScript config file

If you prefer a JavaScript file, create `react-native-brownfield.config.js` and export a plain object with `module.exports`:
Expand All @@ -46,6 +105,14 @@ module.exports = {

If you want schema autocomplete and validation directly in the config file, use `react-native-brownfield.config.json`:

Comment thread
artus9033 marked this conversation as resolved.
> [!TIP]
>
> The schema URL is not required, but it's recommended to include it for autocomplete and validation. If you want to use it, you may need to configure `oss.callstack.com` as a trusted domain in your IDE.
>
> In VS Code, you can do it by adding `oss.callstack.com` to `JSON › Schema Download: Trusted Domains` in your preferences.
>
> In Cursor, it is sufficient to enable `JSON › Schema Download: Enable` in preferences.

```json
{
"$schema": "https://oss.callstack.com/react-native-brownfield/schema.json",
Expand Down Expand Up @@ -102,30 +169,49 @@ All file-based platform options mirror CLI flags, but they use camelCase propert

### Android keys

| Key | Type | Description |
| -------------------- | -------- | -------------------------------------------------------------------- |
| `android.moduleName` | `string` | Android module name used for packaging and publishing AAR artifacts. |
| `android.variant` | `string` | Android build variant, for example `debug` or `freeRelease`. |
| Key | Type | Description |
| -------------------- | -------- | ---------------------------------------------------------------------------------- |
| `android.moduleName` | `string` | Android module name used for packaging, publishing, and Expo prebuild scaffolding. |
| `android.variant` | `string` | Android build variant, for example `debug` or `freeRelease`. |

#### Android Expo keys

| Key | Type | Description |
| -------------------------------- | -------- | ------------------------------------------------------ |
| `android.expo.packageName` | `string` | Package name for the generated Android library module. |
| `android.expo.minSdkVersion` | `number` | Minimum Android SDK supported by the library. |
| `android.expo.targetSdkVersion` | `number` | Target Android SDK version for the library. |
| `android.expo.compileSdkVersion` | `number` | Compile Android SDK version used to build the library. |
| `android.expo.groupId` | `string` | Maven group ID used when publishing the AAR. |
| `android.expo.artifactId` | `string` | Maven artifact ID used when publishing the AAR. |
| `android.expo.version` | `string` | Maven version used when publishing the AAR. |

### iOS keys

| Key | Type | Description |
| ------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| `ios.scheme` | `string` | Xcode scheme used for packaging. |
| `ios.scheme` | `string` | Xcode scheme used for packaging. Also used as the generated framework name during Expo prebuild. |
| `ios.configuration` | `string` | Xcode build configuration, for example `Debug` or `Release`. |
| `ios.target` | `string` | Explicit Xcode target name. |
| `ios.destination` | `string[]` | One or more Xcode destinations, such as `simulator`, `device`, or full destination strings. |
| `ios.buildFolder` | `string` | Custom build output directory. By default, Brownfield uses the `.brownfield/build` path inside the iOS project. |
| `ios.archive` | `boolean` | Creates an archive build suitable for IPA export and distribution. |
| `ios.extraParams` | `string[]` | Extra arguments passed to `xcodebuild`. |
| `ios.exportExtraParams` | `string[]` | Extra arguments passed to the archive export step. |
| `ios.exportOptionsPlist` | `string` | Export options plist filename used during archive export. |
| `ios.installPods` | `boolean` | Controls automatic CocoaPods installation. Set `false` to match `--no-install-pods`. |
| `ios.newArch` | `boolean` | Controls React Native new architecture support. Set `false` to match `--no-new-arch`. |
| `ios.local` | `boolean` | Forces a local `xcodebuild` flow. |
| `ios.usePrebuiltRnCore` | `boolean` | Controls whether iOS packaging uses React Native Apple prebuilts. Omit it to keep Brownfield's version-aware defaults. |
| `ios.addSpmPackage` | `boolean` | Generates a local Swift Package Manager manifest next to the packaged XCFramework outputs. |

#### iOS Expo keys

| Key | Type | Description |
| --------------------------- | -------- | --------------------------------------------------------------- |
| `ios.expo.bundleIdentifier` | `string` | Bundle identifier assigned to the generated framework. |
| `ios.expo.buildSettings` | `object` | Additional Xcode build settings applied to the framework build. |
| `ios.expo.deploymentTarget` | `string` | Minimum iOS version supported by the generated framework. |
| `ios.expo.frameworkVersion` | `string` | Framework version used for Apple build settings. |

## Brownie configuration

The Brownie configuration lives inside the main Brownfield config under the `brownie` key.
Expand Down
4 changes: 1 addition & 3 deletions docs/docs/docs/cli/brownfield.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,10 @@ Available arguments:
| --export-options-plist | Name of the export options file for archiving. Defaults to: `ExportOptions.plist` |
| --build-folder | Location for iOS build artifacts. Corresponds to Xcode's "-derivedDataPath". By default, the '\<iOS project folder>/.brownfield/build' path will be used. |
| --destination | Define destination(s) for the build. You can pass multiple destinations as separate values or repeated use of the flag. Values: "simulator", "device", or xcodebuild destinations |
| --archive | Create an Xcode archive (IPA) of the build, required for uploading to App Store Connect or distributing to TestFlight |
| --add-spm-package | Generate a local `Package.swift` next to the packaged XCFramework outputs so the folder can be added to Xcode as a local Swift Package |
| --add-spm-package | Generate a local `Package.swift` next to the packaged XCFramework outputs so the folder can be added to Xcode as a local Swift Package |
| --use-prebuilt-rn-core [bool] | Controls usage of React Native Apple prebuilt binaries for the packaging Xcode build. Omit for version-aware defaults (see [Getting Started — iOS — React Native Prebuilts](/docs/getting-started/ios#react-native-prebuilts)). Pass `true`, `false`, or use the flag without a value as shorthand for `true`. Supported only for Expo 55+ OR vanilla RN >= 0.81. |
| --no-install-pods | Skip automatic CocoaPods installation |
| --no-new-arch | Run React Native in legacy async architecture |
| --local | Force local build with xcodebuild |

The build directory will be placed in the `<iOS project folder>/.brownfield/build` folder by default and the build outputs (XCFrameworks) will be created in the `<iOS project folder>/.brownfield/package/build` folder:

Expand Down
24 changes: 18 additions & 6 deletions docs/docs/docs/getting-started/expo.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ ReactNativeView(moduleName: "ExpoRNApp")

## Plugin Options

You can pass plugin options through the second item in the `plugins` tuple in `app.json`:
For Expo projects, prefer a [`brownfield.config.json`](/docs/api-reference/configuration#expo-projects) file so the same settings drive both `expo prebuild` and Brownfield CLI commands.

You can still pass plugin options through the second item in the `plugins` tuple in `app.json` when you do **not** use a Brownfield config file:

```json
{
Expand All @@ -186,24 +188,34 @@ You can pass plugin options through the second item in the `plugins` tuple in `a
{
"ios": {
"frameworkName": "MyBrownfieldLib",
"bundleIdentifier": "com.example.app.brownfield",
...
"bundleIdentifier": "com.example.app.brownfield"
},
"android": {
"moduleName": "mybrownfieldlib",
"packageName": "com.example.mybrownfieldlib",
...
"packageName": "com.example.mybrownfieldlib"
}
}
]
]
}
```

:::warning
If you use `brownfield.config.js`, `brownfield.config.json`, or `package.json#brownfield`, do not pass plugin options in `app.json`. Prebuild throws an error when both sources are used.
:::

When using a Brownfield config file, register the plugin without options:

```json
{
"plugins": ["@callstack/react-native-brownfield"]
}
```

### iOS

- `frameworkName` (`string`, default: `"BrownfieldLib"`)
- Name of the generated framework. This is also used as the XCFramework name.
- Name of the generated framework. This is also used as the XCFramework name. In `brownfield.config.*`, use `ios.scheme` instead.
- `bundleIdentifier` (`string`, default: app bundle identifier + `.brownfield`)
- Bundle identifier assigned to the generated framework.
- `buildSettings` (`Record<string, string | boolean | number>`, optional)
Expand Down
16 changes: 11 additions & 5 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
"types": "./dist/types.d.ts",
"default": "./dist/types.js"
},
"./expo-plugin-config": {
"source": "./src/expoPluginConfig.ts",
"types": "./dist/expoPluginConfig.d.ts",
"default": "./dist/expoPluginConfig.js"
},
"./package.json": "./package.json"
},
"scripts": {
Expand Down Expand Up @@ -83,11 +88,11 @@
"@expo/config": "^12.0.13",
"@react-native-community/cli-config": "^20.0.0",
"@react-native-community/cli-config-android": "^20.0.0",
"@rock-js/platform-android": "^0.13.3",
"@rock-js/platform-apple-helpers": "^0.13.3",
"@rock-js/plugin-brownfield-android": "^0.13.3",
"@rock-js/plugin-brownfield-ios": "^0.13.3",
"@rock-js/tools": "^0.13.3",
"@rock-js/platform-android": "^0.13.5",
"@rock-js/platform-apple-helpers": "^0.13.5",
"@rock-js/plugin-brownfield-android": "^0.13.5",
"@rock-js/plugin-brownfield-ios": "^0.13.5",
"@rock-js/tools": "^0.13.5",
"ajv": "^8.20.0",
"commander": "^14.0.3",
"quicktype-core": "^23.2.6",
Expand All @@ -108,6 +113,7 @@
"eslint": "^9.39.3",
"globals": "^17.3.0",
"nodemon": "^3.1.14",
"prettier": "^3.8.1",
"ts-json-schema-generator": "^2.9.0",
"typescript": "5.9.3",
"vitest": "^4.1.4"
Expand Down
Loading
Loading