@callstack/brownfield-cli 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +63 -0
- package/dist/brownfield/commands/packageIos.d.ts.map +1 -1
- package/dist/brownfield/commands/packageIos.js +5 -1
- package/dist/brownfield/utils/index.d.ts +1 -0
- package/dist/brownfield/utils/index.d.ts.map +1 -1
- package/dist/brownfield/utils/index.js +1 -0
- package/dist/brownfield/utils/stripFrameworkBinary.d.ts +9 -0
- package/dist/brownfield/utils/stripFrameworkBinary.d.ts.map +1 -0
- package/dist/brownfield/utils/stripFrameworkBinary.js +107 -0
- package/package.json +7 -3
- package/src/brownfield/commands/packageIos.ts +6 -1
- package/src/brownfield/utils/index.ts +1 -0
- package/src/brownfield/utils/stripFrameworkBinary.ts +149 -0
- package/dist/brownfield/commands/index.d.ts +0 -4
- package/dist/brownfield/commands/index.d.ts.map +0 -1
- package/dist/brownfield/commands/index.js +0 -3
- package/dist/brownfield/types/actionRunner.test.d.ts +0 -2
- package/dist/brownfield/types/actionRunner.test.d.ts.map +0 -1
- package/dist/brownfield/types/actionRunner.test.js +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -54
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @callstack/brownfield-cli
|
|
2
|
+
|
|
3
|
+
## 1.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`2a8563f`](https://github.com/callstack/react-native-brownfield/commit/2a8563f65ed152054ad1290caf963791a368ee9a) Thanks [@okwasniewski](https://github.com/okwasniewski)! - feat: strip framework binaries to avoid duplicate symbol errors
|
|
8
|
+
|
|
9
|
+
## 1.0.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#198](https://github.com/callstack/react-native-brownfield/pull/198) [`c8c903d`](https://github.com/callstack/react-native-brownfield/commit/c8c903d0d2b78a8c06a41213dfbe781a2daf3d25) Thanks [@artus9033](https://github.com/artus9033)! - docs: added README files to all packages
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<a href="https://www.callstack.com/open-source?utm_campaign=generic&utm_source=github&utm_medium=referral&utm_content=react-native-brownfield" align="center">
|
|
2
|
+
<img alt="Brownfield CLI" src="https://github.com/callstack/react-native-brownfield/raw/refs/heads/main/img/brownfield-banner.jpg">
|
|
3
|
+
</a>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
CLI for Brownie and Brownfield, a tool for generating state management code, packaging & publishing Brownfield artifacts.
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
[![Build Status][build-badge]][build]
|
|
12
|
+
[![Version][version-badge]][package]
|
|
13
|
+
[![MIT License][license-badge]][license]
|
|
14
|
+
|
|
15
|
+
[![PRs Welcome][prs-welcome-badge]][prs-welcome]
|
|
16
|
+
[![Chat][chat-badge]][chat]
|
|
17
|
+
[![Code of Conduct][coc-badge]][coc]
|
|
18
|
+
[![Sponsored by Callstack][callstack-badge]][callstack]
|
|
19
|
+
|
|
20
|
+
[![tweet][tweet-badge]][tweet]
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **Shared State** - Single source of truth accessible from both TypeScript and Swift
|
|
25
|
+
- **Type Safety** - Full type inference from TypeScript schema to generated Swift types
|
|
26
|
+
- **React Integration** - `useStore` hook with selector support for optimal re-renders
|
|
27
|
+
- **SwiftUI Integration** - `@UseStore` property wrapper for reactive UI updates
|
|
28
|
+
- **UIKit Support** - Subscribe-based API for imperative UI updates
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
For full documentation, visit [our documentation](https://oss.callstack.com/react-native-brownfield/brownie/overview).
|
|
33
|
+
|
|
34
|
+
<a href="https://www.callstack.com/ebooks/incremental-react-native-adoption-in-native-apps?utm_campaign=brownfield&utm_source=github&utm_medium=referral&utm_content=react-native-brownfield" align="center">
|
|
35
|
+
<img alt="Download a free copy of Incremental React Native adoption in native apps ebook" src="https://github.com/user-attachments/assets/ba42bb29-1e7a-4683-80c5-2602afb1a7e6">
|
|
36
|
+
</a>
|
|
37
|
+
|
|
38
|
+
## Made with ❤️ at Callstack
|
|
39
|
+
|
|
40
|
+
React Native Brownfield is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Callstack](https://callstack.com) is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
|
|
41
|
+
|
|
42
|
+
Like the project? ⚛️ [Join the team](https://callstack.com/careers/?utm_campaign=Senior_RN&utm_source=github&utm_medium=readme) who does amazing stuff for clients and drives React Native Open Source! 🔥
|
|
43
|
+
|
|
44
|
+
<!-- badges -->
|
|
45
|
+
|
|
46
|
+
[build-badge]: https://img.shields.io/circleci/build/github/callstack/react-native-brownfield/master.svg?style=flat-square
|
|
47
|
+
[build]: https://circleci.com/gh/callstack/react-native-brownfield
|
|
48
|
+
[ci]: https://github.com/callstack/react-native-brownfield/actions/workflows/ci.yml/badge.svg
|
|
49
|
+
[version-badge]: https://img.shields.io/npm/v/@callstack/react-native-brownfield.svg?style=flat-square
|
|
50
|
+
[package]: https://www.npmjs.com/package/@callstack/react-native-brownfield
|
|
51
|
+
[license-badge]: https://img.shields.io/npm/l/@callstack/react-native-brownfield.svg?style=flat-square
|
|
52
|
+
[license]: https://opensource.org/licenses/MIT
|
|
53
|
+
[prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
|
|
54
|
+
[prs-welcome]: http://makeapullrequest.com
|
|
55
|
+
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
|
|
56
|
+
[coc]: https://github.com/callstack/react-native-brownfield/blob/master/CODE_OF_CONDUCT.md
|
|
57
|
+
[all-contributors-badge]: https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square
|
|
58
|
+
[chat-badge]: https://img.shields.io/discord/613446453762719798.svg?style=flat-square&colorB=758ED3
|
|
59
|
+
[chat]: https://discord.gg/2SR9Mua
|
|
60
|
+
[tweet-badge]: https://img.shields.io/badge/tweet-%23reactnativebrownfield-blue.svg?style=flat-square&colorB=1DA1F2&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAUCAYAAACXtf2DAAAAAXNSR0IArs4c6QAAAaRJREFUOBGtlM8rBGEYx3cWtRHJRaKcuMtBSitxkCQ3LtzkP9iUUu5ODspRHLhRLtq0FxeicEBC2cOivcge%2FMgan3fNM8bbzL4zm6c%2BPT%2Fe7%2FO8887svrFYBWbbtgWzsAt3sAcpqJFxxF1QV8oJFqFPFst5dLWQAT87oTgPB7DtziFRT1EA4yZolsFkhwjGYFRO8Op0KD8HVe7unoB6PRTBZG8IctAmG1xrHcfkQ2B55sfI%2ByGMXSBqV71xZ8CWdxBxN6ThFuECDEAL%2Bc9HIzDYumVZ966GZnX0SzCZvEqTbkaGywkyFE6hKAsBPhFQ18uPUqh2ggJ%2BUor%2F4M%2F%2FzOC8g6YzR1i%2F8g4vvSI%2ByD7FFNjexQrjHd8%2BnjABI3AU4Wl16TuF1qANGll81jsi5qu%2Bw6XIsCn4ijhU5FmCJpkV6BGNw410hfSf6JKBQ%2FUFxHGYBnWnmOwDwYQ%2BwzdHqO75HtiAMJfaC7ph32FSRJCENUhDHsLaJkL%2FX4wMF4%2BwA5bgAcrZE4sr0Cu9Jq9fxyrvBHWbNkMD5CEHWTjjT2m6r5D92jfmbbKJEWuMMAAAAABJRU5ErkJggg%3D%3D
|
|
61
|
+
[tweet]: https://twitter.com/intent/tweet?text=Check%20out%20react-native-brownfield!%20https://github.com/callstack/react-native-brownfield%20%F0%9F%91%8D
|
|
62
|
+
[callstack-badge]: https://callstack.com/images/callstack-badge.svg
|
|
63
|
+
[callstack]: https://callstack.com/open-source/?utm_source=github.com&utm_medium=referral&utm_campaign=rnbrownfield&utm_term=readme
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageIos.d.ts","sourceRoot":"","sources":["../../../src/brownfield/commands/packageIos.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAGL,YAAY,EACb,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"packageIos.d.ts","sourceRoot":"","sources":["../../../src/brownfield/commands/packageIos.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAGL,YAAY,EACb,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,SAuF7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,cAG7B,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { colorLink, getReactNativeVersion, logger, relativeToCwd, } from '@rock-
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { isBrownieInstalled } from '../../brownie/config.js';
|
|
7
7
|
import { runCodegen } from '../../brownie/commands/codegen.js';
|
|
8
|
-
import { getProjectInfo } from '../utils/index.js';
|
|
8
|
+
import { getProjectInfo, stripFrameworkBinary } from '../utils/index.js';
|
|
9
9
|
import { actionRunner, curryOptions, ExampleUsage, } from '../../shared/index.js';
|
|
10
10
|
export const packageIosCommand = curryOptions(new Command('package:ios').description('Build iOS XCFramework'), getBuildOptions({ platformName: 'ios' }).map((option) => option.name.startsWith('--build-folder')
|
|
11
11
|
? {
|
|
@@ -51,6 +51,10 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
51
51
|
],
|
|
52
52
|
outputPath: brownieOutputPath,
|
|
53
53
|
});
|
|
54
|
+
// Strip the binary from Brownie.xcframework to make it interface-only.
|
|
55
|
+
// This avoids duplicate symbols when consumer apps embed both BrownfieldLib
|
|
56
|
+
// (which contains Brownie symbols) and Brownie.xcframework.
|
|
57
|
+
await stripFrameworkBinary(brownieOutputPath);
|
|
54
58
|
logger.success(`Brownie.xcframework created at ${colorLink(relativeToCwd(brownieOutputPath))}`);
|
|
55
59
|
}
|
|
56
60
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips the binary from an xcframework, keeping only Swift module interfaces.
|
|
3
|
+
* This creates an "interface-only" framework where consumers can import the module
|
|
4
|
+
* but the actual symbols must come from another framework (e.g., BrownfieldLib).
|
|
5
|
+
*
|
|
6
|
+
* @param xcframeworkPath - Path to the .xcframework directory
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripFrameworkBinary(xcframeworkPath: string): void;
|
|
9
|
+
//# sourceMappingURL=stripFrameworkBinary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripFrameworkBinary.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/stripFrameworkBinary.ts"],"names":[],"mappings":"AAuFA;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAsDlE"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
import { logger } from '@rock-js/tools';
|
|
6
|
+
const SLICE_CONFIGS = {
|
|
7
|
+
'ios-arm64': {
|
|
8
|
+
target: 'arm64-apple-ios15.0',
|
|
9
|
+
},
|
|
10
|
+
'ios-arm64_x86_64-simulator': {
|
|
11
|
+
target: 'arm64-apple-ios15.0-simulator',
|
|
12
|
+
additionalTargets: ['x86_64-apple-ios15.0-simulator'],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Creates an empty static library for the given target.
|
|
17
|
+
*/
|
|
18
|
+
function createEmptyStaticLib(target) {
|
|
19
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'framework-strip-'));
|
|
20
|
+
const tempObj = path.join(tempDir, 'empty.o');
|
|
21
|
+
const tempLib = path.join(tempDir, 'empty.a');
|
|
22
|
+
try {
|
|
23
|
+
execSync(`echo "" | xcrun clang -x c -c - -o "${tempObj}" -target ${target}`, {
|
|
24
|
+
stdio: 'pipe',
|
|
25
|
+
});
|
|
26
|
+
execSync(`xcrun ar rcs "${tempLib}" "${tempObj}"`, {
|
|
27
|
+
stdio: 'pipe',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
32
|
+
throw new Error(`Failed to create empty static library for target ${target}: ${error instanceof Error ? error.message : error}`);
|
|
33
|
+
}
|
|
34
|
+
fs.unlinkSync(tempObj);
|
|
35
|
+
return tempLib;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a fat static library combining multiple architectures.
|
|
39
|
+
*/
|
|
40
|
+
function createFatStaticLib(targets) {
|
|
41
|
+
const libs = targets.map((target) => createEmptyStaticLib(target));
|
|
42
|
+
const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'framework-strip-fat-'));
|
|
43
|
+
const outputLib = path.join(outputDir, 'fat.a');
|
|
44
|
+
try {
|
|
45
|
+
execSync(`xcrun lipo -create ${libs.map((l) => `"${l}"`).join(' ')} -output "${outputLib}"`, {
|
|
46
|
+
stdio: 'pipe',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
libs.forEach((lib) => fs.rmSync(path.dirname(lib), { recursive: true }));
|
|
51
|
+
fs.rmSync(outputDir, { recursive: true });
|
|
52
|
+
throw new Error(`Failed to create fat static library: ${error instanceof Error ? error.message : error}`);
|
|
53
|
+
}
|
|
54
|
+
libs.forEach((lib) => {
|
|
55
|
+
fs.unlinkSync(lib);
|
|
56
|
+
fs.rmSync(path.dirname(lib), { recursive: true });
|
|
57
|
+
});
|
|
58
|
+
return outputLib;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Strips the binary from an xcframework, keeping only Swift module interfaces.
|
|
62
|
+
* This creates an "interface-only" framework where consumers can import the module
|
|
63
|
+
* but the actual symbols must come from another framework (e.g., BrownfieldLib).
|
|
64
|
+
*
|
|
65
|
+
* @param xcframeworkPath - Path to the .xcframework directory
|
|
66
|
+
*/
|
|
67
|
+
export function stripFrameworkBinary(xcframeworkPath) {
|
|
68
|
+
if (!fs.existsSync(xcframeworkPath)) {
|
|
69
|
+
throw new Error(`XCFramework not found at: ${xcframeworkPath}`);
|
|
70
|
+
}
|
|
71
|
+
const frameworkName = path.basename(xcframeworkPath, '.xcframework');
|
|
72
|
+
logger.info(`Stripping binary from ${frameworkName}.xcframework (interface-only)...`);
|
|
73
|
+
const slices = fs.readdirSync(xcframeworkPath).filter((entry) => {
|
|
74
|
+
const fullPath = path.join(xcframeworkPath, entry);
|
|
75
|
+
return fs.statSync(fullPath).isDirectory() && entry.startsWith('ios-');
|
|
76
|
+
});
|
|
77
|
+
for (const sliceName of slices) {
|
|
78
|
+
const frameworkDir = path.join(xcframeworkPath, sliceName, `${frameworkName}.framework`);
|
|
79
|
+
const binaryPath = path.join(frameworkDir, frameworkName);
|
|
80
|
+
if (!fs.existsSync(binaryPath)) {
|
|
81
|
+
logger.warn(`No binary found at ${binaryPath}, skipping`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const config = SLICE_CONFIGS[sliceName];
|
|
85
|
+
if (!config) {
|
|
86
|
+
logger.warn(`Unknown slice type: ${sliceName}, skipping`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
let emptyLib;
|
|
90
|
+
if (config.additionalTargets) {
|
|
91
|
+
// Create fat library for multiple architectures
|
|
92
|
+
emptyLib = createFatStaticLib([
|
|
93
|
+
config.target,
|
|
94
|
+
...config.additionalTargets,
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Create single-arch library
|
|
99
|
+
emptyLib = createEmptyStaticLib(config.target);
|
|
100
|
+
}
|
|
101
|
+
// Replace original binary with empty stub
|
|
102
|
+
fs.copyFileSync(emptyLib, binaryPath);
|
|
103
|
+
fs.unlinkSync(emptyLib);
|
|
104
|
+
fs.rmSync(path.dirname(emptyLib), { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
logger.success(`${frameworkName}.xcframework is now interface-only`);
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@callstack/brownfield-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Artur Morys-Magiera <artus9033@gmail.com>",
|
|
6
6
|
"bin": {
|
|
7
|
-
"brownfield": "
|
|
7
|
+
"brownfield": "dist/main.js"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"contributors": [
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"Oskar Kwasniewski <oskarkwasniewski@icloud.com>"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://github.com/callstack/react-native-brownfield",
|
|
15
|
+
"repository": {
|
|
16
|
+
"url": "https://github.com/callstack/react-native-brownfield"
|
|
17
|
+
},
|
|
15
18
|
"description": "Brownfield CLI for React Native, gathering all packages of the RN brownfield ecosystem",
|
|
16
19
|
"exports": {
|
|
17
20
|
".": {
|
|
@@ -53,7 +56,8 @@
|
|
|
53
56
|
"!**/__tests__",
|
|
54
57
|
"!**/__fixtures__",
|
|
55
58
|
"!**/__mocks__",
|
|
56
|
-
"!**/.*"
|
|
59
|
+
"!**/.*",
|
|
60
|
+
"README.md"
|
|
57
61
|
],
|
|
58
62
|
"publishConfig": {
|
|
59
63
|
"access": "public"
|
|
@@ -17,7 +17,7 @@ import { Command } from 'commander';
|
|
|
17
17
|
|
|
18
18
|
import { isBrownieInstalled } from '../../brownie/config.js';
|
|
19
19
|
import { runCodegen } from '../../brownie/commands/codegen.js';
|
|
20
|
-
import { getProjectInfo } from '../utils/index.js';
|
|
20
|
+
import { getProjectInfo, stripFrameworkBinary } from '../utils/index.js';
|
|
21
21
|
import {
|
|
22
22
|
actionRunner,
|
|
23
23
|
curryOptions,
|
|
@@ -101,6 +101,11 @@ export const packageIosCommand = curryOptions(
|
|
|
101
101
|
outputPath: brownieOutputPath,
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
// Strip the binary from Brownie.xcframework to make it interface-only.
|
|
105
|
+
// This avoids duplicate symbols when consumer apps embed both BrownfieldLib
|
|
106
|
+
// (which contains Brownie symbols) and Brownie.xcframework.
|
|
107
|
+
await stripFrameworkBinary(brownieOutputPath);
|
|
108
|
+
|
|
104
109
|
logger.success(
|
|
105
110
|
`Brownie.xcframework created at ${colorLink(relativeToCwd(brownieOutputPath))}`
|
|
106
111
|
);
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
import { logger } from '@rock-js/tools';
|
|
6
|
+
|
|
7
|
+
interface SliceConfig {
|
|
8
|
+
target: string;
|
|
9
|
+
/** Additional targets for fat binaries */
|
|
10
|
+
additionalTargets?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const SLICE_CONFIGS: Record<string, SliceConfig> = {
|
|
14
|
+
'ios-arm64': {
|
|
15
|
+
target: 'arm64-apple-ios15.0',
|
|
16
|
+
},
|
|
17
|
+
'ios-arm64_x86_64-simulator': {
|
|
18
|
+
target: 'arm64-apple-ios15.0-simulator',
|
|
19
|
+
additionalTargets: ['x86_64-apple-ios15.0-simulator'],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates an empty static library for the given target.
|
|
25
|
+
*/
|
|
26
|
+
function createEmptyStaticLib(target: string): string {
|
|
27
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'framework-strip-'));
|
|
28
|
+
const tempObj = path.join(tempDir, 'empty.o');
|
|
29
|
+
const tempLib = path.join(tempDir, 'empty.a');
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
execSync(
|
|
33
|
+
`echo "" | xcrun clang -x c -c - -o "${tempObj}" -target ${target}`,
|
|
34
|
+
{
|
|
35
|
+
stdio: 'pipe',
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
execSync(`xcrun ar rcs "${tempLib}" "${tempObj}"`, {
|
|
40
|
+
stdio: 'pipe',
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Failed to create empty static library for target ${target}: ${error instanceof Error ? error.message : error}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fs.unlinkSync(tempObj);
|
|
50
|
+
|
|
51
|
+
return tempLib;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a fat static library combining multiple architectures.
|
|
56
|
+
*/
|
|
57
|
+
function createFatStaticLib(targets: string[]): string {
|
|
58
|
+
const libs = targets.map((target) => createEmptyStaticLib(target));
|
|
59
|
+
|
|
60
|
+
const outputDir = fs.mkdtempSync(
|
|
61
|
+
path.join(os.tmpdir(), 'framework-strip-fat-')
|
|
62
|
+
);
|
|
63
|
+
const outputLib = path.join(outputDir, 'fat.a');
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
execSync(
|
|
67
|
+
`xcrun lipo -create ${libs.map((l) => `"${l}"`).join(' ')} -output "${outputLib}"`,
|
|
68
|
+
{
|
|
69
|
+
stdio: 'pipe',
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
libs.forEach((lib) => fs.rmSync(path.dirname(lib), { recursive: true }));
|
|
74
|
+
fs.rmSync(outputDir, { recursive: true });
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Failed to create fat static library: ${error instanceof Error ? error.message : error}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
libs.forEach((lib) => {
|
|
81
|
+
fs.unlinkSync(lib);
|
|
82
|
+
fs.rmSync(path.dirname(lib), { recursive: true });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return outputLib;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Strips the binary from an xcframework, keeping only Swift module interfaces.
|
|
90
|
+
* This creates an "interface-only" framework where consumers can import the module
|
|
91
|
+
* but the actual symbols must come from another framework (e.g., BrownfieldLib).
|
|
92
|
+
*
|
|
93
|
+
* @param xcframeworkPath - Path to the .xcframework directory
|
|
94
|
+
*/
|
|
95
|
+
export function stripFrameworkBinary(xcframeworkPath: string): void {
|
|
96
|
+
if (!fs.existsSync(xcframeworkPath)) {
|
|
97
|
+
throw new Error(`XCFramework not found at: ${xcframeworkPath}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const frameworkName = path.basename(xcframeworkPath, '.xcframework');
|
|
101
|
+
|
|
102
|
+
logger.info(
|
|
103
|
+
`Stripping binary from ${frameworkName}.xcframework (interface-only)...`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const slices = fs.readdirSync(xcframeworkPath).filter((entry) => {
|
|
107
|
+
const fullPath = path.join(xcframeworkPath, entry);
|
|
108
|
+
return fs.statSync(fullPath).isDirectory() && entry.startsWith('ios-');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
for (const sliceName of slices) {
|
|
112
|
+
const frameworkDir = path.join(
|
|
113
|
+
xcframeworkPath,
|
|
114
|
+
sliceName,
|
|
115
|
+
`${frameworkName}.framework`
|
|
116
|
+
);
|
|
117
|
+
const binaryPath = path.join(frameworkDir, frameworkName);
|
|
118
|
+
|
|
119
|
+
if (!fs.existsSync(binaryPath)) {
|
|
120
|
+
logger.warn(`No binary found at ${binaryPath}, skipping`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const config = SLICE_CONFIGS[sliceName];
|
|
125
|
+
if (!config) {
|
|
126
|
+
logger.warn(`Unknown slice type: ${sliceName}, skipping`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let emptyLib: string;
|
|
131
|
+
if (config.additionalTargets) {
|
|
132
|
+
// Create fat library for multiple architectures
|
|
133
|
+
emptyLib = createFatStaticLib([
|
|
134
|
+
config.target,
|
|
135
|
+
...config.additionalTargets,
|
|
136
|
+
]);
|
|
137
|
+
} else {
|
|
138
|
+
// Create single-arch library
|
|
139
|
+
emptyLib = createEmptyStaticLib(config.target);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Replace original binary with empty stub
|
|
143
|
+
fs.copyFileSync(emptyLib, binaryPath);
|
|
144
|
+
fs.unlinkSync(emptyLib);
|
|
145
|
+
fs.rmSync(path.dirname(emptyLib), { recursive: true });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
logger.success(`${frameworkName}.xcframework is now interface-only`);
|
|
149
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/brownfield/commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"actionRunner.test.d.ts","sourceRoot":"","sources":["../../../src/brownfield/types/actionRunner.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/cli.d.ts
DELETED
package/dist/cli.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { styleText } from 'node:util';
|
|
3
|
-
import { logger } from '@rock-js/tools';
|
|
4
|
-
import { Command } from 'commander';
|
|
5
|
-
import { ExampleUsage } from './shared/index.js';
|
|
6
|
-
import brownfieldCommands, { groupName as brownfieldCommandsGroupName, } from './brownfield/index.js';
|
|
7
|
-
import brownieCommands, { groupName as brownieCommandsGroupName, } from './brownie/index.js';
|
|
8
|
-
const program = new Command();
|
|
9
|
-
program
|
|
10
|
-
.name(styleText('magenta', 'brownie'))
|
|
11
|
-
.usage(styleText('yellow', '[options] [command]'))
|
|
12
|
-
.description(styleText('magentaBright', 'React Native Brownfield CLI - ') +
|
|
13
|
-
styleText(['magenta', 'bold', 'underline'], 'Brownie'))
|
|
14
|
-
.version(process.env.npm_package_version ?? '0.0.0');
|
|
15
|
-
program
|
|
16
|
-
.optionsGroup('Global options:')
|
|
17
|
-
.option('--verbose', 'enable verbose logging')
|
|
18
|
-
.hook('preAction', () => {
|
|
19
|
-
const opts = program.opts();
|
|
20
|
-
if (opts.verbose) {
|
|
21
|
-
logger.setVerbose(opts.verbose ?? false);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
program.configureHelp({
|
|
25
|
-
styleTitle: (str) => styleText('bold', str),
|
|
26
|
-
styleCommandText: (str) => styleText('cyan', str),
|
|
27
|
-
styleArgumentText: (str) => styleText('yellow', str),
|
|
28
|
-
styleSubcommandText: (str) => styleText('blue', str),
|
|
29
|
-
});
|
|
30
|
-
function registrationHelper(commandsRegistration, groupName) {
|
|
31
|
-
program.commandsGroup(groupName);
|
|
32
|
-
const exampleUsageItems = [];
|
|
33
|
-
Object.values(commandsRegistration).forEach((commandOrExampleUsage) => {
|
|
34
|
-
if (commandOrExampleUsage instanceof Command) {
|
|
35
|
-
// command
|
|
36
|
-
program.addCommand(commandOrExampleUsage);
|
|
37
|
-
}
|
|
38
|
-
else if (commandOrExampleUsage instanceof ExampleUsage) {
|
|
39
|
-
// piece of example usage for the command group
|
|
40
|
-
exampleUsageItems.push(commandOrExampleUsage);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
if (exampleUsageItems.length) {
|
|
44
|
-
const longestUsageItemCommandLength = exampleUsageItems.reduce((max, item) => Math.max(max, item.command.length), 0);
|
|
45
|
-
program.addHelpText('after', `\nExamples:\n${exampleUsageItems.map((item) => `\t ${styleText('dim', item.command.padEnd(longestUsageItemCommandLength))}\t${item.description}`).join('\n')}\n`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
registrationHelper(brownfieldCommands, brownfieldCommandsGroupName);
|
|
49
|
-
registrationHelper(brownieCommands, brownieCommandsGroupName);
|
|
50
|
-
program.commandsGroup('Utility commands').helpCommand('help [command]');
|
|
51
|
-
program.parse(process.argv);
|
|
52
|
-
if (!process.argv.slice(2).length) {
|
|
53
|
-
program.outputHelp();
|
|
54
|
-
}
|