Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
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
2 changes: 1 addition & 1 deletion apps/ExpoApp55/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
}
}
],
["@callstack/react-native-brownfield", { "debug": true }],
"@callstack/react-native-brownfield",
"expo-image",
"expo-font",
"expo-web-browser"
Expand Down
14 changes: 14 additions & 0 deletions apps/ExpoApp55/brownfield.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://oss.callstack.com/react-native-brownfield/schema.json",
"android": {
"moduleName": "brownfieldlib"
},
"ios": {
"scheme": "BrownfieldLib"
},
"verbose": true,
"brownie": {
"kotlin": "./android/brownfieldlib/src/main/java/com/callstack/rnbrownfield/demo/expoapp55/Generated/",
"kotlinPackageName": "com.callstack.rnbrownfield.demo.expoapp55"
}
}
15 changes: 1 addition & 14 deletions apps/ExpoApp55/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,5 @@
"react-test-renderer": "19.2.0",
"typescript": "~5.9.2"
},
"private": true,
"brownfield": {
"brownie": {
"kotlin": "./android/brownfieldlib/src/main/java/com/callstack/rnbrownfield/demo/expoapp55/Generated/",
"kotlinPackageName": "com.callstack.rnbrownfield.demo.expoapp55"
},
"android": {
"moduleName": "brownfieldlib"
},
"ios": {
"scheme": "BrownfieldLib"
},
"verbose": true
}
"private": true
}
102 changes: 91 additions & 11 deletions docs/docs/docs/api-reference/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,78 @@ For example, `--module-name` becomes `moduleName`, `--build-folder` becomes `bui

The CLI supports exactly one configuration source per project:

- `react-native-brownfield.config.js`
- `react-native-brownfield.config.json`
- `package.json` under the `react-native-brownfield` key
- `brownfield.config.js`
- `brownfield.config.json`
- `package.json` under the `brownfield` key

Do not keep more than one of these at the same time.
If the CLI finds multiple sources, it throws an error instead of guessing which one should win.

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`:
If you prefer a JavaScript file, create `brownfield.config.js` and export a plain object with `module.exports`:

```js
/** @type {import('@callstack/react-native-brownfield').BrownfieldConfig} */
Expand All @@ -44,7 +103,7 @@ module.exports = {

## JSON config file

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

> [!TIP]
>
Expand Down Expand Up @@ -110,16 +169,28 @@ 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. |
Expand All @@ -132,6 +203,15 @@ All file-based platform options mirror CLI flags, but they use camelCase propert
| `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 All @@ -144,7 +224,7 @@ Currently supported Brownie keys are:
| `kotlin` | `string` | Directory where generated Kotlin Brownie store files should be written. |
| `kotlinPackageName` | `string` | Kotlin package name used in generated Brownie store files. |

Example inside `react-native-brownfield.config.json`:
Example inside `brownfield.config.json`:

```json
{
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/docs/getting-started/android.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ tasks.named("generateMetadataFileForMavenAarPublication") {

## 7. Create a Brownfield Configuration

Create `react-native-brownfield.config.json` in your project root:
Create `brownfield.config.json` in your project root:

```json
{
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
2 changes: 1 addition & 1 deletion docs/docs/docs/getting-started/ios.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class InternalClassForBundle {}

## 5. Create a Brownfield Configuration

Create `react-native-brownfield.config.json` in your project root:
Create `brownfield.config.json` in your project root:

```json
{
Expand Down
5 changes: 5 additions & 0 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
69 changes: 69 additions & 0 deletions packages/cli/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
"BrownfieldAndroidConfig": {
"additionalProperties": false,
"properties": {
"expo": {
"$ref": "#/definitions/BrownfieldExpoAndroidConfig",
"description": "Expo config plugin options for Android Expo prebuild phase."
},
"moduleName": {
"description": "AAR module name.",
"type": "string"
Expand Down Expand Up @@ -41,6 +45,67 @@
},
"type": "object"
},
"BrownfieldExpoAndroidConfig": {
"additionalProperties": false,
"description": "Expo config plugin options for Android prebuild scaffolding.",
"properties": {
"artifactId": {
"description": "Maven artifact ID used when publishing the AAR.",
"type": "string"
},
"compileSdkVersion": {
"description": "Compile SDK version for the Android library.",
"type": "number"
},
"groupId": {
"description": "Maven group ID used when publishing the AAR.",
"type": "string"
},
"minSdkVersion": {
"description": "Minimum SDK version for the Android library.",
"type": "number"
},
"packageName": {
"description": "The package name for the Android library module.",
"type": "string"
},
"targetSdkVersion": {
"description": "Target SDK version for the Android library.",
"type": "number"
},
"version": {
"description": "Maven version used when publishing the AAR.",
"type": "string"
}
},
"type": "object"
},
"BrownfieldExpoIosConfig": {
"additionalProperties": false,
"description": "Expo config plugin options for iOS prebuild scaffolding.",
"properties": {
"buildSettings": {
"additionalProperties": {
"type": ["string", "boolean", "number"]
},
"description": "Custom build settings applied when building the framework.",
"type": "object"
},
"bundleIdentifier": {
"description": "The bundle identifier for the framework.",
"type": "string"
},
"deploymentTarget": {
"description": "Minimum iOS deployment target for the generated framework.",
"type": "string"
},
"frameworkVersion": {
"description": "Framework version used for Apple build settings.",
"type": "string"
}
},
"type": "object"
},
"BrownfieldIosConfig": {
"additionalProperties": false,
"properties": {
Expand All @@ -63,6 +128,10 @@
},
"type": "array"
},
"expo": {
"$ref": "#/definitions/BrownfieldExpoIosConfig",
"description": "Expo config plugin options for iOS Expo prebuild phase."
},
"exportExtraParams": {
"description": "Custom xcodebuild export archive parameters.",
"items": {
Expand Down
Loading
Loading