Skip to content
Merged
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
4 changes: 0 additions & 4 deletions apps/RNApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,5 @@
},
"engines": {
"node": ">=20"
},
"brownie": {
"kotlin": "./android/BrownfieldLib/src/main/java/com/rnapp/brownfieldlib/Generated",
"kotlinPackageName": "com.rnapp.brownfieldlib"
}
}
5 changes: 0 additions & 5 deletions apps/TesterIntegrated/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,5 @@
},
"engines": {
"node": ">=20"
},
"brownie": {
"swift": "./swift/Generated",
"kotlin": "./kotlin/app/src/main/java/com/callstack/kotlinexample/Generated",
"kotlinPackageName": "com.callstack.kotlinexample"
}
}
55 changes: 5 additions & 50 deletions docs/docs/brownie/codegen.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,15 @@ The Brownfield CLI generates native types from your TypeScript store definitions
## Usage

```bash
brownfield codegen # Generate for all configured platforms
brownfield codegen -p swift # Generate Swift only
brownfield codegen --platform kotlin # Generate Kotlin only (coming soon)
brownfield --help # Show help
brownfield --version # Show version
brownfield codegen # Generate for all configured platforms
brownfield codegen -p swift # Generate Swift only
brownfield --help # Show help
brownfield --version # Show version
```

## Configuration

Add to your app's `package.json`:

```json
{
"brownie": {
"kotlin": "./android/app/src/main/java/com/example/",
"kotlinPackageName": "com.example"
}
}
```

Note: This config is subject to change as Android support is rolled out. Ideally this will be removed in favor of auto-detection.

| Field | Required | Description |
| ------------------- | -------- | ---------------------------------------------------------- |
| `kotlin` | No | Output directory for Kotlin files |
| `kotlinPackageName` | No | Kotlin package name (extracted from path if not specified) |

**Note:** Swift files are always generated to `node_modules/@callstack/brownie/ios/Generated/`. This path is auto-resolved and not configurable.
Swift files are always generated to `node_modules/@callstack/brownie/ios/Generated/`. This path is auto-resolved and not configurable.

## Generated Output

Expand Down Expand Up @@ -73,21 +54,6 @@ The generated struct:
- Conforms to `BrownieStoreProtocol` with auto-generated `storeName`
- Uses mutable `var` properties

### Kotlin Output (Coming Soon)

```kotlin
package com.example

data class BrownfieldStore(
val counter: Double,
val user: User
)

data class User(
val name: String
)
```

## Auto-Generation Hooks

### iOS (Podfile)
Expand All @@ -100,17 +66,6 @@ pre_install do |installer|
end
```

### Android (build.gradle.kts)

Run codegen before build:

```kotlin
tasks.register("generateBrownfieldStore") {
exec { commandLine("npx", "brownie", "codegen", "-p", "kotlin") }
}
preBuild.dependsOn("generateBrownfieldStore")
```

## How It Works

1. CLI recursively finds all `*.brownie.ts` files
Expand Down
47 changes: 6 additions & 41 deletions docs/docs/docs/cli/brownie.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,12 @@ The `brownfield codegen` CLI command generates `@callstack/brownie` (Brownie) st
## Usage

```bash
brownfield codegen # Generate for all configured platforms
brownfield codegen -p swift # Generate Swift only
brownfield codegen --platform kotlin # Generate Kotlin only
brownfield codegen --help # Show help for Brownie state management codegen
brownfield --version # Show version
brownfield codegen # Generate Swift types
brownfield codegen -p swift # Generate Swift only
brownfield codegen --help # Show help for Brownie state management codegen
brownfield --version # Show version
```

## Configuration

Add to your app's `package.json`:

```json
{
"brownie": {
"swift": "./ios/Generated/",
"kotlin": "./android/app/src/main/java/com/example/",
"kotlinPackageName": "com.example"
}
}
```

| Field | Required | Description |
| ------------------- | -------- | ----------------------------------------- |
| `swift` | No\* | Output directory for Swift files |
| `kotlin` | No\* | Output directory for Kotlin files |
| `kotlinPackageName` | No | Kotlin package name (extracted from path) |

\*At least one of `swift` or `kotlin` is required.

## Store Definition

Stores are auto-discovered from `*.brownie.ts` files. Define your store shape using module augmentation:
Expand Down Expand Up @@ -91,17 +68,7 @@ struct BrownfieldStore: Codable {
}
```

**Kotlin** (data class):

```kotlin
package com.example

data class BrownfieldStore (
val counter: Double,
val isLoading: Boolean,
val user: String
)
```
Swift files are always generated to `node_modules/@callstack/brownie/ios/Generated/`. This path is auto-resolved and not configurable.

### Store Discovery

Expand All @@ -127,7 +94,5 @@ JSON Schema
quicktype-core
├──▶ Swift (lang: 'swift', mutable-properties: true)
└──▶ Kotlin (lang: 'kotlin', framework: 'just-types')
└──▶ Swift (lang: 'swift', mutable-properties: true)
```
32 changes: 14 additions & 18 deletions packages/cli/src/brownie/__tests__/commands/codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('runCodegen', () => {
expect(mockGenerateKotlin).not.toHaveBeenCalled();
});

it('generates kotlin files for discovered store', async () => {
it('generates kotlin files when platform is kotlin', async () => {
tempDir = createTempPackageJson({
brownie: {
kotlin: './Generated',
Expand All @@ -101,7 +101,7 @@ describe('runCodegen', () => {
});
mockCwd.mockReturnValue(tempDir);

await runCodegen({});
await runCodegen({ platform: 'kotlin' });

expect(mockGenerateKotlin).toHaveBeenCalledWith({
name: 'TestStore',
Expand All @@ -110,10 +110,10 @@ describe('runCodegen', () => {
outputPath: 'Generated/TestStore.kt',
packageName: 'com.test',
});
expect(mockGenerateSwift).toHaveBeenCalled();
expect(mockGenerateSwift).not.toHaveBeenCalled();
});

it('generates both swift and kotlin when configured', async () => {
it('generates only swift by default even when kotlin is configured', async () => {
tempDir = createTempPackageJson({
brownie: {
kotlin: './Generated',
Expand All @@ -124,7 +124,7 @@ describe('runCodegen', () => {
await runCodegen({});

expect(mockGenerateSwift).toHaveBeenCalled();
expect(mockGenerateKotlin).toHaveBeenCalled();
expect(mockGenerateKotlin).not.toHaveBeenCalled();
});

it('generates only specified platform', async () => {
Expand Down Expand Up @@ -172,19 +172,6 @@ describe('runCodegen', () => {
});
});

it('exits with error for invalid platform', async () => {
tempDir = createTempPackageJson({
brownie: {},
});
mockCwd.mockReturnValue(tempDir);

// @ts-expect-error - testing invalid input
await expect(runCodegen({ platform: 'invalid' })).rejects.toThrow(
'process.exit(1)'
);
expect(mockLoggerError).toHaveBeenCalled();
});

it('exits with error when generator fails', async () => {
tempDir = createTempPackageJson({
brownie: {},
Expand All @@ -206,4 +193,13 @@ describe('runCodegen', () => {

expect(mockGenerateKotlin).not.toHaveBeenCalled();
});

it('works without brownie config in package.json', async () => {
tempDir = createTempPackageJson({});
mockCwd.mockReturnValue(tempDir);

await runCodegen({});

expect(mockGenerateSwift).toHaveBeenCalled();
});
});
7 changes: 3 additions & 4 deletions packages/cli/src/brownie/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ describe('loadConfig', () => {
expect(() => loadConfig()).toThrow('package.json not found');
});

it('throws when brownie config missing', () => {
it('returns empty config when brownie config missing', () => {
tempDir = createTempPackageJson({});
mockCwd.mockReturnValue(tempDir);
expect(() => loadConfig()).toThrow(
'brownie config not found in package.json'
);
const config = loadConfig();
expect(config).toEqual({});
});

it('loads empty config', () => {
Expand Down
8 changes: 3 additions & 5 deletions packages/cli/src/brownie/commands/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,8 @@ export async function runCodegen({ platform }: RunCodegenOptions) {
if (platform) {
platforms = [platform];
} else {
// Only generate Swift by default (Kotlin not yet released)
platforms = ['swift'];
if (config.kotlin) {
platforms.push('kotlin');
}
}

await generateForStore(store, config, platforms, isMultipleStores);
Expand All @@ -131,8 +129,8 @@ export const codegenCommand = new Command('codegen')
.addOption(
new Option(
'-p, --platform <platform>',
'Generate for specific platform (swift, kotlin)'
).choices(['swift', 'kotlin'])
'Generate for specific platform (swift)'
).choices(['swift'])
)
.action(
actionRunner(async (options: RunCodegenOptions) => {
Expand Down
8 changes: 1 addition & 7 deletions packages/cli/src/brownie/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,5 @@ export function loadConfig(): BrownieConfig {
const packageJson: PackageJson = JSON.parse(
fs.readFileSync(packageJsonPath, 'utf-8')
);
const config = packageJson.brownie;

if (!config) {
throw new Error('brownie config not found in package.json');
}

return config;
return packageJson.brownie ?? {};
}