@callstack/brownfield-cli 3.13.2 → 3.15.0
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 +8 -0
- package/dist/brownfield/commands/packageIos.d.ts +1 -0
- package/dist/brownfield/commands/packageIos.d.ts.map +1 -1
- package/dist/brownfield/commands/packageIos.js +33 -2
- package/dist/brownfield/utils/createLocalSpmPackage.d.ts +2 -1
- package/dist/brownfield/utils/createLocalSpmPackage.d.ts.map +1 -1
- package/dist/brownfield/utils/createLocalSpmPackage.js +8 -2
- package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts +9 -0
- package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts.map +1 -0
- package/dist/brownfield/utils/emitExpoSupportXcframeworks.js +70 -0
- package/dist/brownfield/utils/normalizeCopiedXcframework.d.ts +3 -0
- package/dist/brownfield/utils/normalizeCopiedXcframework.d.ts.map +1 -0
- package/dist/brownfield/utils/normalizeCopiedXcframework.js +64 -0
- package/dist/brownfield/utils/prepareLocalSpmArtifacts.d.ts.map +1 -1
- package/dist/brownfield/utils/prepareLocalSpmArtifacts.js +2 -63
- package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts +14 -0
- package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts.map +1 -0
- package/dist/brownfield/utils/supportsPrebuiltExpo.js +20 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/schema.json +4 -0
- package/src/brownfield/commands/packageIos.ts +49 -2
- package/src/brownfield/utils/createLocalSpmPackage.ts +15 -1
- package/src/brownfield/utils/emitExpoSupportXcframeworks.ts +132 -0
- package/src/brownfield/utils/normalizeCopiedXcframework.ts +90 -0
- package/src/brownfield/utils/prepareLocalSpmArtifacts.ts +7 -90
- package/src/brownfield/utils/supportsPrebuiltExpo.ts +34 -0
- package/src/types.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @callstack/brownfield-cli
|
|
2
2
|
|
|
3
|
+
## 3.15.0
|
|
4
|
+
|
|
5
|
+
## 3.14.0
|
|
6
|
+
|
|
7
|
+
### Minor Changes
|
|
8
|
+
|
|
9
|
+
- [#360](https://github.com/callstack/react-native-brownfield/pull/360) [`10ff597`](https://github.com/callstack/react-native-brownfield/commit/10ff597c057c04aa093b4e5ae8c5334b169d3d56) Thanks [@adamTrz](https://github.com/adamTrz)! - Expo SDK 56 support
|
|
10
|
+
|
|
3
11
|
## 3.13.2
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { ExampleUsage } from '../../shared/index.js';
|
|
3
3
|
export declare function parseUsePrebuiltRnCoreArgument(value: string | boolean): boolean;
|
|
4
|
+
export declare function parseUsePrebuiltExpoArgument(value: string | boolean): boolean;
|
|
4
5
|
export declare const packageIosCommand: Command;
|
|
5
6
|
export declare const packageIosExample: ExampleUsage;
|
|
6
7
|
//# sourceMappingURL=packageIos.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageIos.d.ts","sourceRoot":"","sources":["../../../src/brownfield/commands/packageIos.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"packageIos.d.ts","sourceRoot":"","sources":["../../../src/brownfield/commands/packageIos.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAO5C,OAAO,EAGL,YAAY,EACb,MAAM,uBAAuB,CAAC;AA4C/B,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,MAAM,GAAG,OAAO,GACtB,OAAO,CAET;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,GAAG,OAAO,GACtB,OAAO,CAET;AAcD,eAAO,MAAM,iBAAiB,SA8R3B,CAAC;AAEJ,eAAO,MAAM,iBAAiB,cAG7B,CAAC"}
|
|
@@ -7,6 +7,7 @@ import { Command, Option } from 'commander';
|
|
|
7
7
|
import { runExpoPrebuildIfNeeded } from '../utils/expo.js';
|
|
8
8
|
import { findProjectRoot } from '../utils/paths.js';
|
|
9
9
|
import { getProjectInfo } from '../utils/project.js';
|
|
10
|
+
import { supportsPrebuiltExpo } from '../utils/supportsPrebuiltExpo.js';
|
|
10
11
|
import { supportsPrebuiltRNCore } from '../utils/supportsPrebuiltRNCore.js';
|
|
11
12
|
import { actionRunner, curryOptions, ExampleUsage, } from '../../shared/index.js';
|
|
12
13
|
import { runBrownieCodegenIfApplicable } from '../../brownie/helpers/runBrownieCodegenIfApplicable.js';
|
|
@@ -15,13 +16,19 @@ import { copyDebugBundleToSimulatorSlice } from '../utils/copyDebugBundleToSimul
|
|
|
15
16
|
import { resolvePackagedFrameworkName } from '../utils/resolvePackagedFrameworkName.js';
|
|
16
17
|
import { stripFrameworkBinary } from '../utils/stripFrameworkBinary.js';
|
|
17
18
|
import { createLocalSpmPackage } from '../utils/createLocalSpmPackage.js';
|
|
19
|
+
import { emitExpoSupportXcframeworks } from '../utils/emitExpoSupportXcframeworks.js';
|
|
18
20
|
import { mergeBrownfieldConfigWithOptions } from '../../config.js';
|
|
21
|
+
import { removeCodeSignatureArtifacts } from '../utils/normalizeCopiedXcframework.js';
|
|
19
22
|
/** Help text for `--use-prebuilt-rn-core` (keep in sync with docs/docs/docs/getting-started/ios.mdx, "React Native Prebuilts" section). */
|
|
20
23
|
const USE_PREBUILT_RN_CORE_HELP = 'Whether the Xcode build for packaging should use React Native Apple prebuilt binaries (via CocoaPods). ' +
|
|
21
24
|
'If you omit this flag, Brownfield follows version-aware defaults: for React Native 0.84 and newer, prebuilts are enabled by default; for RN 0.83, they are disabled unless you opt in. ' +
|
|
22
25
|
'Pass true or false to force either behavior. Use the flag without a value as shorthand for true (same as `--use-prebuilt-rn-core true`). ' +
|
|
23
26
|
'See the Brownfield iOS guide for details: https://oss.callstack.com/react-native-brownfield/docs/getting-started/ios#react-native-prebuilts';
|
|
24
|
-
|
|
27
|
+
/** Help text for `--use-prebuilt-expo`. */
|
|
28
|
+
const USE_PREBUILT_EXPO_HELP = 'Whether the Xcode build for packaging should use prebuilt Expo support XCFrameworks. ' +
|
|
29
|
+
'If you omit this flag, Brownfield follows version-aware defaults for supported Expo projects. ' +
|
|
30
|
+
'Pass true or false to force either behavior. Use the flag without a value as shorthand for true (same as `--use-prebuilt-expo true`).';
|
|
31
|
+
function parseBooleanOptionArgument(flagName, value) {
|
|
25
32
|
if (typeof value === 'boolean') {
|
|
26
33
|
return value;
|
|
27
34
|
}
|
|
@@ -32,7 +39,13 @@ export function parseUsePrebuiltRnCoreArgument(value) {
|
|
|
32
39
|
if (normalized === 'false' || normalized === '0') {
|
|
33
40
|
return false;
|
|
34
41
|
}
|
|
35
|
-
throw new RockError(`Invalid value for
|
|
42
|
+
throw new RockError(`Invalid value for ${flagName}: expected true or false, received "${value}"`);
|
|
43
|
+
}
|
|
44
|
+
export function parseUsePrebuiltRnCoreArgument(value) {
|
|
45
|
+
return parseBooleanOptionArgument('--use-prebuilt-rn-core', value);
|
|
46
|
+
}
|
|
47
|
+
export function parseUsePrebuiltExpoArgument(value) {
|
|
48
|
+
return parseBooleanOptionArgument('--use-prebuilt-expo', value);
|
|
36
49
|
}
|
|
37
50
|
function getPackagedFrameworkResolutionFailureMessage({ resolution, candidates, }) {
|
|
38
51
|
return resolution === 'ambiguous'
|
|
@@ -49,6 +62,9 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
49
62
|
.addOption(new Option('--use-prebuilt-rn-core [bool]', USE_PREBUILT_RN_CORE_HELP)
|
|
50
63
|
.preset(true)
|
|
51
64
|
.argParser(parseUsePrebuiltRnCoreArgument))
|
|
65
|
+
.addOption(new Option('--use-prebuilt-expo [bool]', USE_PREBUILT_EXPO_HELP)
|
|
66
|
+
.preset(true)
|
|
67
|
+
.argParser(parseUsePrebuiltExpoArgument))
|
|
52
68
|
.addOption(new Option('--add-spm-package', 'Generate a local Swift Package Manager manifest next to the packaged XCFramework outputs'))
|
|
53
69
|
.action(actionRunner(async (cliOptions) => {
|
|
54
70
|
const options = mergeBrownfieldConfigWithOptions(cliOptions, 'ios');
|
|
@@ -56,10 +72,15 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
56
72
|
await runExpoPrebuildIfNeeded({ projectRoot, platform: 'ios' });
|
|
57
73
|
const { platformConfig, userConfig } = getProjectInfo('ios');
|
|
58
74
|
const prebuiltRNCoreSupport = supportsPrebuiltRNCore({ projectRoot });
|
|
75
|
+
const prebuiltExpoSupport = supportsPrebuiltExpo({ projectRoot });
|
|
59
76
|
// version-aware default when the flag is omitted (see ios.mdx "React Native Prebuilts")
|
|
60
77
|
options.usePrebuiltRnCore ??= prebuiltRNCoreSupport.supported
|
|
61
78
|
? prebuiltRNCoreSupport.enabledByDefault
|
|
62
79
|
: false;
|
|
80
|
+
// version-aware default when the flag is omitted
|
|
81
|
+
options.usePrebuiltExpo ??= prebuiltExpoSupport.supported
|
|
82
|
+
? prebuiltExpoSupport.enabledByDefault
|
|
83
|
+
: false;
|
|
63
84
|
if (prebuiltRNCoreSupport) {
|
|
64
85
|
logger.info(`${options.usePrebuiltRnCore ? 'Using' : 'Not using'} prebuilt RN core`);
|
|
65
86
|
if (!prebuiltRNCoreSupport.enabledByDefault &&
|
|
@@ -70,6 +91,9 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
70
91
|
if (options.usePrebuiltRnCore && !prebuiltRNCoreSupport.supported) {
|
|
71
92
|
throw new RockError(prebuiltRNCoreSupport.reason);
|
|
72
93
|
}
|
|
94
|
+
if (options.usePrebuiltExpo && !prebuiltExpoSupport.supported) {
|
|
95
|
+
throw new RockError(prebuiltExpoSupport.reason);
|
|
96
|
+
}
|
|
73
97
|
if (!userConfig.project.ios) {
|
|
74
98
|
throw new Error('iOS project not found.');
|
|
75
99
|
}
|
|
@@ -106,6 +130,11 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
106
130
|
skipCache: true, // cache is dependent on existence of Rock config file
|
|
107
131
|
usePrebuiltRNCore: options.usePrebuiltRnCore,
|
|
108
132
|
}, platformConfig);
|
|
133
|
+
emitExpoSupportXcframeworks({
|
|
134
|
+
projectRoot,
|
|
135
|
+
packageDir,
|
|
136
|
+
usePrebuiltExpo: options.usePrebuiltExpo,
|
|
137
|
+
});
|
|
109
138
|
const productsPath = path.join(options.buildFolder, 'Build', 'Products');
|
|
110
139
|
const { frameworkName, resolution, candidates } = resolvePackagedFrameworkName({
|
|
111
140
|
explicitScheme: options.scheme,
|
|
@@ -178,10 +207,12 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
178
207
|
stripFrameworkBinary(brownfieldNavigationOutputPath);
|
|
179
208
|
logger.success(`BrownfieldNavigation.xcframework created at ${colorLink(relativeToCwd(brownfieldNavigationOutputPath))}`);
|
|
180
209
|
}
|
|
210
|
+
removeCodeSignatureArtifacts(packageDir);
|
|
181
211
|
if (options.addSpmPackage) {
|
|
182
212
|
const { packageManifestPath } = createLocalSpmPackage({
|
|
183
213
|
packageDir,
|
|
184
214
|
frameworkName: frameworkName ?? undefined,
|
|
215
|
+
usePrebuiltExpo: options.usePrebuiltExpo,
|
|
185
216
|
});
|
|
186
217
|
logger.success(`Local SPM package manifest created at ${colorLink(relativeToCwd(packageManifestPath))}`);
|
|
187
218
|
logger.info(`Add the local package folder in Xcode: ${colorLink(relativeToCwd(packageDir))}`);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
type CreateLocalSpmPackageOptions = {
|
|
2
2
|
packageDir: string;
|
|
3
3
|
frameworkName?: string;
|
|
4
|
+
usePrebuiltExpo?: boolean;
|
|
4
5
|
};
|
|
5
6
|
type CreateLocalSpmPackageResult = {
|
|
6
7
|
packageManifestPath: string;
|
|
7
8
|
};
|
|
8
|
-
export declare function createLocalSpmPackage({ packageDir, frameworkName, }: CreateLocalSpmPackageOptions): CreateLocalSpmPackageResult;
|
|
9
|
+
export declare function createLocalSpmPackage({ packageDir, frameworkName, usePrebuiltExpo, }: CreateLocalSpmPackageOptions): CreateLocalSpmPackageResult;
|
|
9
10
|
export {};
|
|
10
11
|
//# sourceMappingURL=createLocalSpmPackage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createLocalSpmPackage.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/createLocalSpmPackage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createLocalSpmPackage.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/createLocalSpmPackage.ts"],"names":[],"mappings":"AAYA,KAAK,4BAA4B,GAAG;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,KAAK,2BAA2B,GAAG;IACjC,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AA0JF,wBAAgB,qBAAqB,CAAC,EACpC,UAAU,EACV,aAAa,EACb,eAAsB,GACvB,EAAE,4BAA4B,GAAG,2BAA2B,CA2C5D"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { prepareLocalSpmArtifacts, SPM_ARTIFACTS_DIR_NAME, } from './prepareLocalSpmArtifacts.js';
|
|
4
|
+
import { ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES, getExpoSupportXcframeworkNames, } from './emitExpoSupportXcframeworks.js';
|
|
4
5
|
const RESERVED_FRAMEWORK_NAMES = new Set([
|
|
5
6
|
'hermes',
|
|
6
7
|
'hermesvm',
|
|
@@ -9,6 +10,7 @@ const RESERVED_FRAMEWORK_NAMES = new Set([
|
|
|
9
10
|
'BrownfieldNavigation',
|
|
10
11
|
'React',
|
|
11
12
|
'ReactNativeDependencies',
|
|
13
|
+
...ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES,
|
|
12
14
|
]);
|
|
13
15
|
function requireXcframework(packageDir, name) {
|
|
14
16
|
const xcframeworkPath = path.join(packageDir, `${name}.xcframework`);
|
|
@@ -71,7 +73,9 @@ ${binaryTargets}
|
|
|
71
73
|
`;
|
|
72
74
|
}
|
|
73
75
|
function renderReadme({ packageName, libraryName, targetNames, }) {
|
|
74
|
-
const frameworks = targetNames
|
|
76
|
+
const frameworks = targetNames
|
|
77
|
+
.map((targetName) => `- \`${targetName}\``)
|
|
78
|
+
.join('\n');
|
|
75
79
|
return `# ${packageName}
|
|
76
80
|
|
|
77
81
|
This is a generated local Swift Package Manager package for the packaged React Native brownfield artifacts.
|
|
@@ -103,14 +107,16 @@ If Xcode builds your host app but the simulator installation fails, check the ho
|
|
|
103
107
|
This folder is generated by \`brownfield package:ios --add-spm-package\`. Re-run that command whenever the packaged XCFrameworks change so this package stays in sync.
|
|
104
108
|
`;
|
|
105
109
|
}
|
|
106
|
-
export function createLocalSpmPackage({ packageDir, frameworkName, }) {
|
|
110
|
+
export function createLocalSpmPackage({ packageDir, frameworkName, usePrebuiltExpo = true, }) {
|
|
107
111
|
const resolvedFrameworkName = resolveAppFrameworkName(packageDir, frameworkName);
|
|
112
|
+
const expoSupportXcframeworkNames = getExpoSupportXcframeworkNames(usePrebuiltExpo);
|
|
108
113
|
const targetNames = [
|
|
109
114
|
resolvedFrameworkName,
|
|
110
115
|
requireHermesXcframework(packageDir),
|
|
111
116
|
requireXcframework(packageDir, 'ReactBrownfield'),
|
|
112
117
|
optionalXcframework(packageDir, 'Brownie'),
|
|
113
118
|
optionalXcframework(packageDir, 'BrownfieldNavigation'),
|
|
119
|
+
...expoSupportXcframeworkNames.map((frameworkName) => optionalXcframework(packageDir, frameworkName)),
|
|
114
120
|
optionalXcframework(packageDir, 'React'),
|
|
115
121
|
optionalXcframework(packageDir, 'ReactNativeDependencies'),
|
|
116
122
|
].filter((targetName) => targetName !== null);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const EXPO_SUPPORT_XCFRAMEWORK_NAMES: readonly ["ExpoModulesJSI", "ExpoFileSystem", "ExpoFont", "ExpoModulesCore", "ExpoImage", "ExpoModulesWorklets"];
|
|
2
|
+
export declare const ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES: readonly ["ExpoModulesJSI", "ExpoFileSystem", "ExpoFont", "ExpoModulesCore", "ExpoImage", "ExpoModulesWorklets", "SDWebImage", "SDWebImageSVGCoder", "SDWebImageWebPCoder", "SDWebImageAVIFCoder", "libavif"];
|
|
3
|
+
export declare function getExpoSupportXcframeworkNames(usePrebuiltExpo: boolean): readonly ["ExpoModulesJSI", "ExpoFileSystem", "ExpoFont", "ExpoModulesCore", "ExpoImage", "ExpoModulesWorklets", "SDWebImage", "SDWebImageSVGCoder", "SDWebImageWebPCoder", "SDWebImageAVIFCoder", "libavif"] | readonly ["ExpoModulesJSI"];
|
|
4
|
+
export declare function emitExpoSupportXcframeworks({ projectRoot, packageDir, usePrebuiltExpo, }: {
|
|
5
|
+
projectRoot: string;
|
|
6
|
+
packageDir: string;
|
|
7
|
+
usePrebuiltExpo?: boolean;
|
|
8
|
+
}): boolean;
|
|
9
|
+
//# sourceMappingURL=emitExpoSupportXcframeworks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitExpoSupportXcframeworks.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/emitExpoSupportXcframeworks.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,8BAA8B,kHAOjC,CAAC;AAEX,eAAO,MAAM,kCAAkC,+MAGrC,CAAC;AAEX,wBAAgB,8BAA8B,CAAC,eAAe,EAAE,OAAO,+OAItE;AA8CD,wBAAgB,2BAA2B,CAAC,EAC1C,WAAW,EACX,UAAU,EACV,eAAsB,GACvB,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,WAkCA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { RockError } from '@rock-js/tools';
|
|
4
|
+
import { normalizeCopiedXcframework } from './normalizeCopiedXcframework.js';
|
|
5
|
+
import { getExpoSdkMajor, isExpoProject } from './project.js';
|
|
6
|
+
import { MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO } from './supportsPrebuiltExpo.js';
|
|
7
|
+
/**
|
|
8
|
+
* Swift support XCFrameworks
|
|
9
|
+
*
|
|
10
|
+
* These are required for the Expo Image module to work.
|
|
11
|
+
*
|
|
12
|
+
* TODO: This needs investigation and maybe a discussion with the Expo team.
|
|
13
|
+
*/
|
|
14
|
+
const SWIFT_SUPPORT_XCFRAMEWORK_NAMES = [
|
|
15
|
+
'SDWebImage',
|
|
16
|
+
'SDWebImageSVGCoder',
|
|
17
|
+
'SDWebImageWebPCoder',
|
|
18
|
+
'SDWebImageAVIFCoder',
|
|
19
|
+
'libavif',
|
|
20
|
+
];
|
|
21
|
+
export const EXPO_SUPPORT_XCFRAMEWORK_NAMES = [
|
|
22
|
+
'ExpoModulesJSI',
|
|
23
|
+
'ExpoFileSystem',
|
|
24
|
+
'ExpoFont',
|
|
25
|
+
'ExpoModulesCore',
|
|
26
|
+
'ExpoImage',
|
|
27
|
+
'ExpoModulesWorklets'
|
|
28
|
+
];
|
|
29
|
+
export const ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES = [
|
|
30
|
+
...EXPO_SUPPORT_XCFRAMEWORK_NAMES,
|
|
31
|
+
...SWIFT_SUPPORT_XCFRAMEWORK_NAMES,
|
|
32
|
+
];
|
|
33
|
+
export function getExpoSupportXcframeworkNames(usePrebuiltExpo) {
|
|
34
|
+
return usePrebuiltExpo
|
|
35
|
+
? ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES
|
|
36
|
+
: ['ExpoModulesJSI'];
|
|
37
|
+
}
|
|
38
|
+
function resolveExpoFrameworkSourcePath(projectRoot, frameworkName) {
|
|
39
|
+
if (frameworkName === 'ExpoModulesJSI') {
|
|
40
|
+
return path.join(projectRoot, 'node_modules', 'expo-modules-jsi', 'apple', 'Products', 'ExpoModulesJSI.xcframework');
|
|
41
|
+
}
|
|
42
|
+
if (SWIFT_SUPPORT_XCFRAMEWORK_NAMES.includes(frameworkName)) {
|
|
43
|
+
return path.join(projectRoot, 'ios', 'Pods', 'ExpoImage', `${frameworkName}.xcframework`);
|
|
44
|
+
}
|
|
45
|
+
if (EXPO_SUPPORT_XCFRAMEWORK_NAMES.includes(frameworkName)) {
|
|
46
|
+
return path.join(projectRoot, 'ios', 'Pods', frameworkName, `${frameworkName}.xcframework`);
|
|
47
|
+
}
|
|
48
|
+
throw new RockError(`Unsupported Expo XCFramework: ${frameworkName}`);
|
|
49
|
+
}
|
|
50
|
+
export function emitExpoSupportXcframeworks({ projectRoot, packageDir, usePrebuiltExpo = true, }) {
|
|
51
|
+
if (!isExpoProject(projectRoot)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const expoSdkMajor = getExpoSdkMajor(projectRoot);
|
|
55
|
+
if (expoSdkMajor === null ||
|
|
56
|
+
expoSdkMajor < MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
for (const frameworkName of getExpoSupportXcframeworkNames(usePrebuiltExpo)) {
|
|
60
|
+
const sourcePath = resolveExpoFrameworkSourcePath(projectRoot, frameworkName);
|
|
61
|
+
if (!fs.existsSync(sourcePath)) {
|
|
62
|
+
throw new RockError(`Expected Expo SDK ${MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO}+ XCFramework not found: ${frameworkName}.xcframework at ${path.relative(projectRoot, sourcePath)}`);
|
|
63
|
+
}
|
|
64
|
+
const destinationPath = path.join(packageDir, `${frameworkName}.xcframework`);
|
|
65
|
+
fs.rmSync(destinationPath, { recursive: true, force: true });
|
|
66
|
+
fs.cpSync(sourcePath, destinationPath, { recursive: true });
|
|
67
|
+
normalizeCopiedXcframework(destinationPath);
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeCopiedXcframework.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/normalizeCopiedXcframework.ts"],"names":[],"mappings":"AAIA,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,QAkB3D;AAyCD,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,MAAM,QA0BjE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
export function removeCodeSignatureArtifacts(dirPath) {
|
|
5
|
+
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
6
|
+
const entryPath = path.join(dirPath, entry.name);
|
|
7
|
+
if (entry.isDirectory()) {
|
|
8
|
+
if (entry.name === '_CodeSignature') {
|
|
9
|
+
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
removeCodeSignatureArtifacts(entryPath);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (entry.name === 'CodeResources') {
|
|
16
|
+
fs.rmSync(entryPath, { force: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function resolveFrameworkExecutablePath(frameworkDir, frameworkName) {
|
|
21
|
+
const directExecutablePath = path.join(frameworkDir, frameworkName);
|
|
22
|
+
if (fs.existsSync(directExecutablePath)) {
|
|
23
|
+
return directExecutablePath;
|
|
24
|
+
}
|
|
25
|
+
const versionedExecutablePath = path.join(frameworkDir, 'Versions', 'Current', frameworkName);
|
|
26
|
+
if (fs.existsSync(versionedExecutablePath)) {
|
|
27
|
+
return versionedExecutablePath;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function removeExecutableSignature(executablePath) {
|
|
32
|
+
try {
|
|
33
|
+
execFileSync('codesign', ['--remove-signature', executablePath], {
|
|
34
|
+
stdio: 'pipe',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof Error &&
|
|
39
|
+
error.message.includes('code object is not signed at all')) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function normalizeCopiedXcframework(xcframeworkPath) {
|
|
46
|
+
removeCodeSignatureArtifacts(xcframeworkPath);
|
|
47
|
+
for (const sliceName of fs.readdirSync(xcframeworkPath)) {
|
|
48
|
+
const slicePath = path.join(xcframeworkPath, sliceName);
|
|
49
|
+
if (!fs.statSync(slicePath).isDirectory()) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
for (const entry of fs.readdirSync(slicePath)) {
|
|
53
|
+
if (!entry.endsWith('.framework')) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const frameworkName = path.basename(entry, '.framework');
|
|
57
|
+
const frameworkDir = path.join(slicePath, entry);
|
|
58
|
+
const executablePath = resolveFrameworkExecutablePath(frameworkDir, frameworkName);
|
|
59
|
+
if (executablePath) {
|
|
60
|
+
removeExecutableSignature(executablePath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepareLocalSpmArtifacts.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/prepareLocalSpmArtifacts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prepareLocalSpmArtifacts.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/prepareLocalSpmArtifacts.ts"],"names":[],"mappings":"AAKA,KAAK,+BAA+B,GAAG;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AAEtD,wBAAgB,wBAAwB,CAAC,EACvC,UAAU,EACV,WAAW,GACZ,EAAE,+BAA+B,UAkBjC"}
|
|
@@ -1,68 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { normalizeCopiedXcframework } from './normalizeCopiedXcframework.js';
|
|
4
4
|
export const SPM_ARTIFACTS_DIR_NAME = 'spm-artifacts';
|
|
5
|
-
function removeCodeSignatureArtifacts(dirPath) {
|
|
6
|
-
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
7
|
-
const entryPath = path.join(dirPath, entry.name);
|
|
8
|
-
if (entry.isDirectory()) {
|
|
9
|
-
if (entry.name === '_CodeSignature') {
|
|
10
|
-
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
11
|
-
continue;
|
|
12
|
-
}
|
|
13
|
-
removeCodeSignatureArtifacts(entryPath);
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (entry.name === 'CodeResources') {
|
|
17
|
-
fs.rmSync(entryPath, { force: true });
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function resolveFrameworkExecutablePath(frameworkDir, frameworkName) {
|
|
22
|
-
const directExecutablePath = path.join(frameworkDir, frameworkName);
|
|
23
|
-
if (fs.existsSync(directExecutablePath)) {
|
|
24
|
-
return directExecutablePath;
|
|
25
|
-
}
|
|
26
|
-
const versionedExecutablePath = path.join(frameworkDir, 'Versions', 'Current', frameworkName);
|
|
27
|
-
if (fs.existsSync(versionedExecutablePath)) {
|
|
28
|
-
return versionedExecutablePath;
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
function removeExecutableSignature(executablePath) {
|
|
33
|
-
try {
|
|
34
|
-
execFileSync('codesign', ['--remove-signature', executablePath], {
|
|
35
|
-
stdio: 'pipe',
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
if (error instanceof Error &&
|
|
40
|
-
error.message.includes('code object is not signed at all')) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function normalizeCopiedXcframeworkSignature(xcframeworkPath) {
|
|
47
|
-
removeCodeSignatureArtifacts(xcframeworkPath);
|
|
48
|
-
for (const sliceName of fs.readdirSync(xcframeworkPath)) {
|
|
49
|
-
const slicePath = path.join(xcframeworkPath, sliceName);
|
|
50
|
-
if (!fs.statSync(slicePath).isDirectory()) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
for (const entry of fs.readdirSync(slicePath)) {
|
|
54
|
-
if (!entry.endsWith('.framework')) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const frameworkName = path.basename(entry, '.framework');
|
|
58
|
-
const frameworkDir = path.join(slicePath, entry);
|
|
59
|
-
const executablePath = resolveFrameworkExecutablePath(frameworkDir, frameworkName);
|
|
60
|
-
if (executablePath) {
|
|
61
|
-
removeExecutableSignature(executablePath);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
5
|
export function prepareLocalSpmArtifacts({ packageDir, targetNames, }) {
|
|
67
6
|
const spmArtifactsDir = path.join(packageDir, SPM_ARTIFACTS_DIR_NAME);
|
|
68
7
|
fs.rmSync(spmArtifactsDir, { recursive: true, force: true });
|
|
@@ -71,7 +10,7 @@ export function prepareLocalSpmArtifacts({ packageDir, targetNames, }) {
|
|
|
71
10
|
const sourcePath = path.join(packageDir, `${targetName}.xcframework`);
|
|
72
11
|
const destinationPath = path.join(spmArtifactsDir, `${targetName}.xcframework`);
|
|
73
12
|
fs.renameSync(sourcePath, destinationPath);
|
|
74
|
-
|
|
13
|
+
normalizeCopiedXcframework(destinationPath);
|
|
75
14
|
}
|
|
76
15
|
return spmArtifactsDir;
|
|
77
16
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO = 56;
|
|
2
|
+
export type PrebuiltExpoSupportResult = {
|
|
3
|
+
supported: true;
|
|
4
|
+
enabledByDefault: boolean;
|
|
5
|
+
reason?: never;
|
|
6
|
+
} | {
|
|
7
|
+
supported: false;
|
|
8
|
+
enabledByDefault?: never;
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function supportsPrebuiltExpo({ projectRoot, }: {
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
}): PrebuiltExpoSupportResult;
|
|
14
|
+
//# sourceMappingURL=supportsPrebuiltExpo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supportsPrebuiltExpo.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/supportsPrebuiltExpo.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,MAAM,MAAM,yBAAyB,GACjC;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAC9D;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,wBAAgB,oBAAoB,CAAC,EACnC,WAAW,GACZ,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,yBAAyB,CAqB5B"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getExpoSdkMajor, isExpoProject } from './project.js';
|
|
2
|
+
export const MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO = 56;
|
|
3
|
+
export function supportsPrebuiltExpo({ projectRoot, }) {
|
|
4
|
+
if (!isExpoProject(projectRoot)) {
|
|
5
|
+
return { supported: true, enabledByDefault: false };
|
|
6
|
+
}
|
|
7
|
+
const expoSdkMajor = getExpoSdkMajor(projectRoot);
|
|
8
|
+
if (expoSdkMajor === null ||
|
|
9
|
+
expoSdkMajor < MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO) {
|
|
10
|
+
const sdkLabel = expoSdkMajor === null ? 'unknown' : String(expoSdkMajor);
|
|
11
|
+
return {
|
|
12
|
+
supported: false,
|
|
13
|
+
reason: `--use-prebuilt-expo is unsupported in Expo SDK ${sdkLabel}: packaging brownfield with Expo prebuilts requires Expo SDK ${MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO} or newer.`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
supported: true,
|
|
18
|
+
enabledByDefault: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type BrownieConfig = {
|
|
|
23
23
|
export type PackageIosOptions = Omit<AppleBuildFlags, 'archive' | 'local'> & {
|
|
24
24
|
/** Set when `--use-prebuilt-rn-core` is passed; omitted when the flag is absent (Rock applies RN version defaults). */
|
|
25
25
|
usePrebuiltRnCore?: boolean;
|
|
26
|
+
/** Set when `--use-prebuilt-expo` is passed; omitted when the flag is absent (Rock applies Expo version defaults). */
|
|
27
|
+
usePrebuiltExpo?: boolean;
|
|
26
28
|
/** When set, generate a local Swift Package Manager manifest next to the packaged XCFramework outputs. */
|
|
27
29
|
addSpmPackage?: boolean;
|
|
28
30
|
};
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAErF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAAC;IAC5C;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG;IAC3E,uHAAuH;IACvH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,uBAAuB,GACnE,OAAO,CAAC,eAAe,CAAC,CAAC;AAC3B,MAAM,MAAM,+BAA+B,GAAG,uBAAuB,GACnE,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAChC,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAC/D,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE7B;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;IAE1D;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,EACxD,MAAM,uBAAuB,CAC9B,GAAG;IACF;;OAEG;IACH,IAAI,CAAC,EAAE,2BAA2B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,OAAO,CAAC,iBAAiB,CAAC,EAC1B,MAAM,uBAAuB,CAC9B,GAAG;IACF;;OAEG;IACH,IAAI,CAAC,EAAE,uBAAuB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,wBAAwB,GACrD,uBAAuB,GACvB,OAAO,CAAC;IACN;;OAEG;IACH,OAAO,EAAE,uBAAuB,CAAC;IAEjC;;OAEG;IACH,GAAG,EAAE,mBAAmB,CAAC;IAEzB;;OAEG;IACH,OAAO,EAAE,aAAa,CAAC;CACxB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAErF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAAC;IAC5C;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG;IAC3E,uHAAuH;IACvH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,sHAAsH;IACtH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,uBAAuB,GACnE,OAAO,CAAC,eAAe,CAAC,CAAC;AAC3B,MAAM,MAAM,+BAA+B,GAAG,uBAAuB,GACnE,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAChC,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAC/D,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE7B;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;IAE1D;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,EACxD,MAAM,uBAAuB,CAC9B,GAAG;IACF;;OAEG;IACH,IAAI,CAAC,EAAE,2BAA2B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,OAAO,CAAC,iBAAiB,CAAC,EAC1B,MAAM,uBAAuB,CAC9B,GAAG;IACF;;OAEG;IACH,IAAI,CAAC,EAAE,uBAAuB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,wBAAwB,GACrD,uBAAuB,GACvB,OAAO,CAAC;IACN;;OAEG;IACH,OAAO,EAAE,uBAAuB,CAAC;IAEjC;;OAEG;IACH,GAAG,EAAE,mBAAmB,CAAC;IAEzB;;OAEG;IACH,OAAO,EAAE,aAAa,CAAC;CACxB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@callstack/brownfield-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Artur Morys-Magiera <artus9033@gmail.com>",
|
|
6
6
|
"bin": {
|
|
@@ -88,11 +88,11 @@
|
|
|
88
88
|
"@expo/config": "^12.0.13",
|
|
89
89
|
"@react-native-community/cli-config": "^20.0.0",
|
|
90
90
|
"@react-native-community/cli-config-android": "^20.0.0",
|
|
91
|
-
"@rock-js/platform-android": "^0.
|
|
92
|
-
"@rock-js/platform-apple-helpers": "^0.
|
|
93
|
-
"@rock-js/plugin-brownfield-android": "^0.
|
|
94
|
-
"@rock-js/plugin-brownfield-ios": "^0.
|
|
95
|
-
"@rock-js/tools": "^0.
|
|
91
|
+
"@rock-js/platform-android": "^0.14.0",
|
|
92
|
+
"@rock-js/platform-apple-helpers": "^0.14.0",
|
|
93
|
+
"@rock-js/plugin-brownfield-android": "^0.14.0",
|
|
94
|
+
"@rock-js/plugin-brownfield-ios": "^0.14.0",
|
|
95
|
+
"@rock-js/tools": "^0.14.0",
|
|
96
96
|
"ajv": "^8.20.0",
|
|
97
97
|
"commander": "^14.0.3",
|
|
98
98
|
"quicktype-core": "^23.2.6",
|
package/schema.json
CHANGED
|
@@ -170,6 +170,10 @@
|
|
|
170
170
|
"description": "Xcode target to use.",
|
|
171
171
|
"type": "string"
|
|
172
172
|
},
|
|
173
|
+
"usePrebuiltExpo": {
|
|
174
|
+
"description": "Set when `--use-prebuilt-expo` is passed; omitted when the flag is absent (Rock applies Expo version defaults).",
|
|
175
|
+
"type": "boolean"
|
|
176
|
+
},
|
|
173
177
|
"usePrebuiltRnCore": {
|
|
174
178
|
"description": "Set when `--use-prebuilt-rn-core` is passed; omitted when the flag is absent (Rock applies RN version defaults).",
|
|
175
179
|
"type": "boolean"
|
|
@@ -19,6 +19,7 @@ import { Command, Option } from 'commander';
|
|
|
19
19
|
import { runExpoPrebuildIfNeeded } from '../utils/expo.js';
|
|
20
20
|
import { findProjectRoot } from '../utils/paths.js';
|
|
21
21
|
import { getProjectInfo } from '../utils/project.js';
|
|
22
|
+
import { supportsPrebuiltExpo } from '../utils/supportsPrebuiltExpo.js';
|
|
22
23
|
import { supportsPrebuiltRNCore } from '../utils/supportsPrebuiltRNCore.js';
|
|
23
24
|
import {
|
|
24
25
|
actionRunner,
|
|
@@ -32,7 +33,9 @@ import { resolvePackagedFrameworkName } from '../utils/resolvePackagedFrameworkN
|
|
|
32
33
|
import { stripFrameworkBinary } from '../utils/stripFrameworkBinary.js';
|
|
33
34
|
import type { PackageIosOptions } from '../../types.js';
|
|
34
35
|
import { createLocalSpmPackage } from '../utils/createLocalSpmPackage.js';
|
|
36
|
+
import { emitExpoSupportXcframeworks } from '../utils/emitExpoSupportXcframeworks.js';
|
|
35
37
|
import { mergeBrownfieldConfigWithOptions } from '../../config.js';
|
|
38
|
+
import { removeCodeSignatureArtifacts } from '../utils/normalizeCopiedXcframework.js';
|
|
36
39
|
|
|
37
40
|
/** Help text for `--use-prebuilt-rn-core` (keep in sync with docs/docs/docs/getting-started/ios.mdx, "React Native Prebuilts" section). */
|
|
38
41
|
const USE_PREBUILT_RN_CORE_HELP =
|
|
@@ -41,7 +44,14 @@ const USE_PREBUILT_RN_CORE_HELP =
|
|
|
41
44
|
'Pass true or false to force either behavior. Use the flag without a value as shorthand for true (same as `--use-prebuilt-rn-core true`). ' +
|
|
42
45
|
'See the Brownfield iOS guide for details: https://oss.callstack.com/react-native-brownfield/docs/getting-started/ios#react-native-prebuilts';
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
/** Help text for `--use-prebuilt-expo`. */
|
|
48
|
+
const USE_PREBUILT_EXPO_HELP =
|
|
49
|
+
'Whether the Xcode build for packaging should use prebuilt Expo support XCFrameworks. ' +
|
|
50
|
+
'If you omit this flag, Brownfield follows version-aware defaults for supported Expo projects. ' +
|
|
51
|
+
'Pass true or false to force either behavior. Use the flag without a value as shorthand for true (same as `--use-prebuilt-expo true`).';
|
|
52
|
+
|
|
53
|
+
function parseBooleanOptionArgument(
|
|
54
|
+
flagName: string,
|
|
45
55
|
value: string | boolean
|
|
46
56
|
): boolean {
|
|
47
57
|
if (typeof value === 'boolean') {
|
|
@@ -55,10 +65,22 @@ export function parseUsePrebuiltRnCoreArgument(
|
|
|
55
65
|
return false;
|
|
56
66
|
}
|
|
57
67
|
throw new RockError(
|
|
58
|
-
`Invalid value for
|
|
68
|
+
`Invalid value for ${flagName}: expected true or false, received "${value}"`
|
|
59
69
|
);
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
export function parseUsePrebuiltRnCoreArgument(
|
|
73
|
+
value: string | boolean
|
|
74
|
+
): boolean {
|
|
75
|
+
return parseBooleanOptionArgument('--use-prebuilt-rn-core', value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function parseUsePrebuiltExpoArgument(
|
|
79
|
+
value: string | boolean
|
|
80
|
+
): boolean {
|
|
81
|
+
return parseBooleanOptionArgument('--use-prebuilt-expo', value);
|
|
82
|
+
}
|
|
83
|
+
|
|
62
84
|
function getPackagedFrameworkResolutionFailureMessage({
|
|
63
85
|
resolution,
|
|
64
86
|
candidates,
|
|
@@ -89,6 +111,11 @@ export const packageIosCommand = curryOptions(
|
|
|
89
111
|
.preset(true)
|
|
90
112
|
.argParser(parseUsePrebuiltRnCoreArgument)
|
|
91
113
|
)
|
|
114
|
+
.addOption(
|
|
115
|
+
new Option('--use-prebuilt-expo [bool]', USE_PREBUILT_EXPO_HELP)
|
|
116
|
+
.preset(true)
|
|
117
|
+
.argParser(parseUsePrebuiltExpoArgument)
|
|
118
|
+
)
|
|
92
119
|
.addOption(
|
|
93
120
|
new Option(
|
|
94
121
|
'--add-spm-package',
|
|
@@ -105,12 +132,18 @@ export const packageIosCommand = curryOptions(
|
|
|
105
132
|
const { platformConfig, userConfig } = getProjectInfo('ios');
|
|
106
133
|
|
|
107
134
|
const prebuiltRNCoreSupport = supportsPrebuiltRNCore({ projectRoot });
|
|
135
|
+
const prebuiltExpoSupport = supportsPrebuiltExpo({ projectRoot });
|
|
108
136
|
|
|
109
137
|
// version-aware default when the flag is omitted (see ios.mdx "React Native Prebuilts")
|
|
110
138
|
options.usePrebuiltRnCore ??= prebuiltRNCoreSupport.supported
|
|
111
139
|
? prebuiltRNCoreSupport.enabledByDefault
|
|
112
140
|
: false;
|
|
113
141
|
|
|
142
|
+
// version-aware default when the flag is omitted
|
|
143
|
+
options.usePrebuiltExpo ??= prebuiltExpoSupport.supported
|
|
144
|
+
? prebuiltExpoSupport.enabledByDefault
|
|
145
|
+
: false;
|
|
146
|
+
|
|
114
147
|
if (prebuiltRNCoreSupport) {
|
|
115
148
|
logger.info(
|
|
116
149
|
`${options.usePrebuiltRnCore ? 'Using' : 'Not using'} prebuilt RN core`
|
|
@@ -130,6 +163,10 @@ export const packageIosCommand = curryOptions(
|
|
|
130
163
|
throw new RockError(prebuiltRNCoreSupport.reason);
|
|
131
164
|
}
|
|
132
165
|
|
|
166
|
+
if (options.usePrebuiltExpo && !prebuiltExpoSupport.supported) {
|
|
167
|
+
throw new RockError(prebuiltExpoSupport.reason);
|
|
168
|
+
}
|
|
169
|
+
|
|
133
170
|
if (!userConfig.project.ios) {
|
|
134
171
|
throw new Error('iOS project not found.');
|
|
135
172
|
}
|
|
@@ -184,6 +221,12 @@ export const packageIosCommand = curryOptions(
|
|
|
184
221
|
platformConfig
|
|
185
222
|
);
|
|
186
223
|
|
|
224
|
+
emitExpoSupportXcframeworks({
|
|
225
|
+
projectRoot,
|
|
226
|
+
packageDir,
|
|
227
|
+
usePrebuiltExpo: options.usePrebuiltExpo,
|
|
228
|
+
});
|
|
229
|
+
|
|
187
230
|
const productsPath = path.join(options.buildFolder, 'Build', 'Products');
|
|
188
231
|
const { frameworkName, resolution, candidates } =
|
|
189
232
|
resolvePackagedFrameworkName({
|
|
@@ -244,6 +287,7 @@ export const packageIosCommand = curryOptions(
|
|
|
244
287
|
packageDir,
|
|
245
288
|
'ReactBrownfield.xcframework'
|
|
246
289
|
);
|
|
290
|
+
|
|
247
291
|
if (fs.existsSync(reactBrownfieldXcframeworkPath)) {
|
|
248
292
|
// Strip the binary from ReactBrownfield.xcframework to make it interface-only.
|
|
249
293
|
// This avoids duplicate symbols when consumer apps embed both BrownfieldLib
|
|
@@ -315,10 +359,13 @@ export const packageIosCommand = curryOptions(
|
|
|
315
359
|
);
|
|
316
360
|
}
|
|
317
361
|
|
|
362
|
+
removeCodeSignatureArtifacts(packageDir);
|
|
363
|
+
|
|
318
364
|
if (options.addSpmPackage) {
|
|
319
365
|
const { packageManifestPath } = createLocalSpmPackage({
|
|
320
366
|
packageDir,
|
|
321
367
|
frameworkName: frameworkName ?? undefined,
|
|
368
|
+
usePrebuiltExpo: options.usePrebuiltExpo,
|
|
322
369
|
});
|
|
323
370
|
|
|
324
371
|
logger.success(
|
|
@@ -5,10 +5,15 @@ import {
|
|
|
5
5
|
prepareLocalSpmArtifacts,
|
|
6
6
|
SPM_ARTIFACTS_DIR_NAME,
|
|
7
7
|
} from './prepareLocalSpmArtifacts.js';
|
|
8
|
+
import {
|
|
9
|
+
ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES,
|
|
10
|
+
getExpoSupportXcframeworkNames,
|
|
11
|
+
} from './emitExpoSupportXcframeworks.js';
|
|
8
12
|
|
|
9
13
|
type CreateLocalSpmPackageOptions = {
|
|
10
14
|
packageDir: string;
|
|
11
15
|
frameworkName?: string;
|
|
16
|
+
usePrebuiltExpo?: boolean;
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
type CreateLocalSpmPackageResult = {
|
|
@@ -23,6 +28,7 @@ const RESERVED_FRAMEWORK_NAMES = new Set([
|
|
|
23
28
|
'BrownfieldNavigation',
|
|
24
29
|
'React',
|
|
25
30
|
'ReactNativeDependencies',
|
|
31
|
+
...ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES,
|
|
26
32
|
]);
|
|
27
33
|
|
|
28
34
|
function requireXcframework(packageDir: string, name: string) {
|
|
@@ -130,7 +136,9 @@ function renderReadme({
|
|
|
130
136
|
libraryName: string;
|
|
131
137
|
targetNames: string[];
|
|
132
138
|
}) {
|
|
133
|
-
const frameworks = targetNames
|
|
139
|
+
const frameworks = targetNames
|
|
140
|
+
.map((targetName) => `- \`${targetName}\``)
|
|
141
|
+
.join('\n');
|
|
134
142
|
|
|
135
143
|
return `# ${packageName}
|
|
136
144
|
|
|
@@ -167,17 +175,23 @@ This folder is generated by \`brownfield package:ios --add-spm-package\`. Re-run
|
|
|
167
175
|
export function createLocalSpmPackage({
|
|
168
176
|
packageDir,
|
|
169
177
|
frameworkName,
|
|
178
|
+
usePrebuiltExpo = true,
|
|
170
179
|
}: CreateLocalSpmPackageOptions): CreateLocalSpmPackageResult {
|
|
171
180
|
const resolvedFrameworkName = resolveAppFrameworkName(
|
|
172
181
|
packageDir,
|
|
173
182
|
frameworkName
|
|
174
183
|
);
|
|
184
|
+
const expoSupportXcframeworkNames =
|
|
185
|
+
getExpoSupportXcframeworkNames(usePrebuiltExpo);
|
|
175
186
|
const targetNames = [
|
|
176
187
|
resolvedFrameworkName,
|
|
177
188
|
requireHermesXcframework(packageDir),
|
|
178
189
|
requireXcframework(packageDir, 'ReactBrownfield'),
|
|
179
190
|
optionalXcframework(packageDir, 'Brownie'),
|
|
180
191
|
optionalXcframework(packageDir, 'BrownfieldNavigation'),
|
|
192
|
+
...expoSupportXcframeworkNames.map((frameworkName) =>
|
|
193
|
+
optionalXcframework(packageDir, frameworkName)
|
|
194
|
+
),
|
|
181
195
|
optionalXcframework(packageDir, 'React'),
|
|
182
196
|
optionalXcframework(packageDir, 'ReactNativeDependencies'),
|
|
183
197
|
].filter((targetName): targetName is string => targetName !== null);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { RockError } from '@rock-js/tools';
|
|
5
|
+
|
|
6
|
+
import { normalizeCopiedXcframework } from './normalizeCopiedXcframework.js';
|
|
7
|
+
import { getExpoSdkMajor, isExpoProject } from './project.js';
|
|
8
|
+
|
|
9
|
+
import { MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO } from './supportsPrebuiltExpo.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Swift support XCFrameworks
|
|
13
|
+
*
|
|
14
|
+
* These are required for the Expo Image module to work.
|
|
15
|
+
*
|
|
16
|
+
* TODO: This needs investigation and maybe a discussion with the Expo team.
|
|
17
|
+
*/
|
|
18
|
+
const SWIFT_SUPPORT_XCFRAMEWORK_NAMES = [
|
|
19
|
+
'SDWebImage',
|
|
20
|
+
'SDWebImageSVGCoder',
|
|
21
|
+
'SDWebImageWebPCoder',
|
|
22
|
+
'SDWebImageAVIFCoder',
|
|
23
|
+
'libavif',
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export const EXPO_SUPPORT_XCFRAMEWORK_NAMES = [
|
|
27
|
+
'ExpoModulesJSI',
|
|
28
|
+
'ExpoFileSystem',
|
|
29
|
+
'ExpoFont',
|
|
30
|
+
'ExpoModulesCore',
|
|
31
|
+
'ExpoImage',
|
|
32
|
+
'ExpoModulesWorklets'
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
export const ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES = [
|
|
36
|
+
...EXPO_SUPPORT_XCFRAMEWORK_NAMES,
|
|
37
|
+
...SWIFT_SUPPORT_XCFRAMEWORK_NAMES,
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
export function getExpoSupportXcframeworkNames(usePrebuiltExpo: boolean) {
|
|
41
|
+
return usePrebuiltExpo
|
|
42
|
+
? ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES
|
|
43
|
+
: (['ExpoModulesJSI'] as const);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type SwiftSupportXcframeworkName =
|
|
47
|
+
(typeof SWIFT_SUPPORT_XCFRAMEWORK_NAMES)[number];
|
|
48
|
+
|
|
49
|
+
type ExpoSupportXcframeworkName =
|
|
50
|
+
(typeof EXPO_SUPPORT_XCFRAMEWORK_NAMES)[number];
|
|
51
|
+
|
|
52
|
+
function resolveExpoFrameworkSourcePath(
|
|
53
|
+
projectRoot: string,
|
|
54
|
+
frameworkName: SwiftSupportXcframeworkName | ExpoSupportXcframeworkName
|
|
55
|
+
) {
|
|
56
|
+
if (frameworkName === 'ExpoModulesJSI') {
|
|
57
|
+
return path.join(
|
|
58
|
+
projectRoot,
|
|
59
|
+
'node_modules',
|
|
60
|
+
'expo-modules-jsi',
|
|
61
|
+
'apple',
|
|
62
|
+
'Products',
|
|
63
|
+
'ExpoModulesJSI.xcframework'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (SWIFT_SUPPORT_XCFRAMEWORK_NAMES.includes(frameworkName as SwiftSupportXcframeworkName)) {
|
|
68
|
+
return path.join(
|
|
69
|
+
projectRoot,
|
|
70
|
+
'ios',
|
|
71
|
+
'Pods',
|
|
72
|
+
'ExpoImage',
|
|
73
|
+
`${frameworkName}.xcframework`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (EXPO_SUPPORT_XCFRAMEWORK_NAMES.includes(frameworkName as ExpoSupportXcframeworkName)) {
|
|
78
|
+
return path.join(
|
|
79
|
+
projectRoot,
|
|
80
|
+
'ios',
|
|
81
|
+
'Pods',
|
|
82
|
+
frameworkName,
|
|
83
|
+
`${frameworkName}.xcframework`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw new RockError(`Unsupported Expo XCFramework: ${frameworkName}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function emitExpoSupportXcframeworks({
|
|
91
|
+
projectRoot,
|
|
92
|
+
packageDir,
|
|
93
|
+
usePrebuiltExpo = true,
|
|
94
|
+
}: {
|
|
95
|
+
projectRoot: string;
|
|
96
|
+
packageDir: string;
|
|
97
|
+
usePrebuiltExpo?: boolean;
|
|
98
|
+
}) {
|
|
99
|
+
if (!isExpoProject(projectRoot)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const expoSdkMajor = getExpoSdkMajor(projectRoot);
|
|
104
|
+
if (
|
|
105
|
+
expoSdkMajor === null ||
|
|
106
|
+
expoSdkMajor < MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO
|
|
107
|
+
) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const frameworkName of getExpoSupportXcframeworkNames(usePrebuiltExpo)) {
|
|
112
|
+
const sourcePath = resolveExpoFrameworkSourcePath(
|
|
113
|
+
projectRoot,
|
|
114
|
+
frameworkName
|
|
115
|
+
);
|
|
116
|
+
if (!fs.existsSync(sourcePath)) {
|
|
117
|
+
throw new RockError(
|
|
118
|
+
`Expected Expo SDK ${MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO}+ XCFramework not found: ${frameworkName}.xcframework at ${path.relative(projectRoot, sourcePath)}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const destinationPath = path.join(
|
|
123
|
+
packageDir,
|
|
124
|
+
`${frameworkName}.xcframework`
|
|
125
|
+
);
|
|
126
|
+
fs.rmSync(destinationPath, { recursive: true, force: true });
|
|
127
|
+
fs.cpSync(sourcePath, destinationPath, { recursive: true });
|
|
128
|
+
normalizeCopiedXcframework(destinationPath);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
export function removeCodeSignatureArtifacts(dirPath: string) {
|
|
6
|
+
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
7
|
+
const entryPath = path.join(dirPath, entry.name);
|
|
8
|
+
|
|
9
|
+
if (entry.isDirectory()) {
|
|
10
|
+
if (entry.name === '_CodeSignature') {
|
|
11
|
+
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
removeCodeSignatureArtifacts(entryPath);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (entry.name === 'CodeResources') {
|
|
20
|
+
fs.rmSync(entryPath, { force: true });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveFrameworkExecutablePath(
|
|
26
|
+
frameworkDir: string,
|
|
27
|
+
frameworkName: string
|
|
28
|
+
) {
|
|
29
|
+
const directExecutablePath = path.join(frameworkDir, frameworkName);
|
|
30
|
+
if (fs.existsSync(directExecutablePath)) {
|
|
31
|
+
return directExecutablePath;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const versionedExecutablePath = path.join(
|
|
35
|
+
frameworkDir,
|
|
36
|
+
'Versions',
|
|
37
|
+
'Current',
|
|
38
|
+
frameworkName
|
|
39
|
+
);
|
|
40
|
+
if (fs.existsSync(versionedExecutablePath)) {
|
|
41
|
+
return versionedExecutablePath;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function removeExecutableSignature(executablePath: string) {
|
|
48
|
+
try {
|
|
49
|
+
execFileSync('codesign', ['--remove-signature', executablePath], {
|
|
50
|
+
stdio: 'pipe',
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (
|
|
54
|
+
error instanceof Error &&
|
|
55
|
+
error.message.includes('code object is not signed at all')
|
|
56
|
+
) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function normalizeCopiedXcframework(xcframeworkPath: string) {
|
|
65
|
+
removeCodeSignatureArtifacts(xcframeworkPath);
|
|
66
|
+
|
|
67
|
+
for (const sliceName of fs.readdirSync(xcframeworkPath)) {
|
|
68
|
+
const slicePath = path.join(xcframeworkPath, sliceName);
|
|
69
|
+
if (!fs.statSync(slicePath).isDirectory()) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const entry of fs.readdirSync(slicePath)) {
|
|
74
|
+
if (!entry.endsWith('.framework')) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const frameworkName = path.basename(entry, '.framework');
|
|
79
|
+
const frameworkDir = path.join(slicePath, entry);
|
|
80
|
+
const executablePath = resolveFrameworkExecutablePath(
|
|
81
|
+
frameworkDir,
|
|
82
|
+
frameworkName
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (executablePath) {
|
|
86
|
+
removeExecutableSignature(executablePath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { normalizeCopiedXcframework } from './normalizeCopiedXcframework.js';
|
|
4
5
|
|
|
5
6
|
type PrepareLocalSpmArtifactsOptions = {
|
|
6
7
|
packageDir: string;
|
|
@@ -9,93 +10,6 @@ type PrepareLocalSpmArtifactsOptions = {
|
|
|
9
10
|
|
|
10
11
|
export const SPM_ARTIFACTS_DIR_NAME = 'spm-artifacts';
|
|
11
12
|
|
|
12
|
-
function removeCodeSignatureArtifacts(dirPath: string) {
|
|
13
|
-
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
14
|
-
const entryPath = path.join(dirPath, entry.name);
|
|
15
|
-
|
|
16
|
-
if (entry.isDirectory()) {
|
|
17
|
-
if (entry.name === '_CodeSignature') {
|
|
18
|
-
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
removeCodeSignatureArtifacts(entryPath);
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (entry.name === 'CodeResources') {
|
|
27
|
-
fs.rmSync(entryPath, { force: true });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function resolveFrameworkExecutablePath(
|
|
33
|
-
frameworkDir: string,
|
|
34
|
-
frameworkName: string
|
|
35
|
-
) {
|
|
36
|
-
const directExecutablePath = path.join(frameworkDir, frameworkName);
|
|
37
|
-
if (fs.existsSync(directExecutablePath)) {
|
|
38
|
-
return directExecutablePath;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const versionedExecutablePath = path.join(
|
|
42
|
-
frameworkDir,
|
|
43
|
-
'Versions',
|
|
44
|
-
'Current',
|
|
45
|
-
frameworkName
|
|
46
|
-
);
|
|
47
|
-
if (fs.existsSync(versionedExecutablePath)) {
|
|
48
|
-
return versionedExecutablePath;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function removeExecutableSignature(executablePath: string) {
|
|
55
|
-
try {
|
|
56
|
-
execFileSync('codesign', ['--remove-signature', executablePath], {
|
|
57
|
-
stdio: 'pipe',
|
|
58
|
-
});
|
|
59
|
-
} catch (error) {
|
|
60
|
-
if (
|
|
61
|
-
error instanceof Error &&
|
|
62
|
-
error.message.includes('code object is not signed at all')
|
|
63
|
-
) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function normalizeCopiedXcframeworkSignature(xcframeworkPath: string) {
|
|
72
|
-
removeCodeSignatureArtifacts(xcframeworkPath);
|
|
73
|
-
|
|
74
|
-
for (const sliceName of fs.readdirSync(xcframeworkPath)) {
|
|
75
|
-
const slicePath = path.join(xcframeworkPath, sliceName);
|
|
76
|
-
if (!fs.statSync(slicePath).isDirectory()) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
for (const entry of fs.readdirSync(slicePath)) {
|
|
81
|
-
if (!entry.endsWith('.framework')) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const frameworkName = path.basename(entry, '.framework');
|
|
86
|
-
const frameworkDir = path.join(slicePath, entry);
|
|
87
|
-
const executablePath = resolveFrameworkExecutablePath(
|
|
88
|
-
frameworkDir,
|
|
89
|
-
frameworkName
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
if (executablePath) {
|
|
93
|
-
removeExecutableSignature(executablePath);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
13
|
export function prepareLocalSpmArtifacts({
|
|
100
14
|
packageDir,
|
|
101
15
|
targetNames,
|
|
@@ -107,10 +21,13 @@ export function prepareLocalSpmArtifacts({
|
|
|
107
21
|
|
|
108
22
|
for (const targetName of targetNames) {
|
|
109
23
|
const sourcePath = path.join(packageDir, `${targetName}.xcframework`);
|
|
110
|
-
const destinationPath = path.join(
|
|
24
|
+
const destinationPath = path.join(
|
|
25
|
+
spmArtifactsDir,
|
|
26
|
+
`${targetName}.xcframework`
|
|
27
|
+
);
|
|
111
28
|
|
|
112
29
|
fs.renameSync(sourcePath, destinationPath);
|
|
113
|
-
|
|
30
|
+
normalizeCopiedXcframework(destinationPath);
|
|
114
31
|
}
|
|
115
32
|
|
|
116
33
|
return spmArtifactsDir;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getExpoSdkMajor, isExpoProject } from './project.js';
|
|
2
|
+
|
|
3
|
+
export const MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO = 56;
|
|
4
|
+
|
|
5
|
+
export type PrebuiltExpoSupportResult =
|
|
6
|
+
| { supported: true; enabledByDefault: boolean; reason?: never }
|
|
7
|
+
| { supported: false; enabledByDefault?: never; reason: string };
|
|
8
|
+
|
|
9
|
+
export function supportsPrebuiltExpo({
|
|
10
|
+
projectRoot,
|
|
11
|
+
}: {
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
}): PrebuiltExpoSupportResult {
|
|
14
|
+
if (!isExpoProject(projectRoot)) {
|
|
15
|
+
return { supported: true, enabledByDefault: false };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const expoSdkMajor = getExpoSdkMajor(projectRoot);
|
|
19
|
+
if (
|
|
20
|
+
expoSdkMajor === null ||
|
|
21
|
+
expoSdkMajor < MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO
|
|
22
|
+
) {
|
|
23
|
+
const sdkLabel = expoSdkMajor === null ? 'unknown' : String(expoSdkMajor);
|
|
24
|
+
return {
|
|
25
|
+
supported: false,
|
|
26
|
+
reason: `--use-prebuilt-expo is unsupported in Expo SDK ${sdkLabel}: packaging brownfield with Expo prebuilts requires Expo SDK ${MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO} or newer.`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
supported: true,
|
|
32
|
+
enabledByDefault: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -33,6 +33,9 @@ export type PackageIosOptions = Omit<AppleBuildFlags, 'archive' | 'local'> & {
|
|
|
33
33
|
/** Set when `--use-prebuilt-rn-core` is passed; omitted when the flag is absent (Rock applies RN version defaults). */
|
|
34
34
|
usePrebuiltRnCore?: boolean;
|
|
35
35
|
|
|
36
|
+
/** Set when `--use-prebuilt-expo` is passed; omitted when the flag is absent (Rock applies Expo version defaults). */
|
|
37
|
+
usePrebuiltExpo?: boolean;
|
|
38
|
+
|
|
36
39
|
/** When set, generate a local Swift Package Manager manifest next to the packaged XCFramework outputs. */
|
|
37
40
|
addSpmPackage?: boolean;
|
|
38
41
|
};
|