@callstack/brownfield-cli 6.0.0 → 6.0.1
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 +16 -0
- package/dist/brownfield/commands/packageIos.d.ts +2 -0
- package/dist/brownfield/commands/packageIos.d.ts.map +1 -1
- package/dist/brownfield/commands/packageIos.js +102 -18
- package/dist/brownfield/utils/copyDebugBundleToSimulatorSlice.d.ts +4 -1
- package/dist/brownfield/utils/copyDebugBundleToSimulatorSlice.d.ts.map +1 -1
- package/dist/brownfield/utils/copyDebugBundleToSimulatorSlice.js +6 -1
- package/dist/brownfield/utils/destinationSdks.d.ts +36 -0
- package/dist/brownfield/utils/destinationSdks.d.ts.map +1 -0
- package/dist/brownfield/utils/destinationSdks.js +70 -0
- package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts +9 -1
- package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts.map +1 -1
- package/dist/brownfield/utils/emitExpoSupportXcframeworks.js +27 -5
- package/dist/brownfield/utils/expo.d.ts +7 -0
- package/dist/brownfield/utils/expo.d.ts.map +1 -1
- package/dist/brownfield/utils/expo.js +28 -1
- package/dist/brownfield/utils/project.d.ts +14 -0
- package/dist/brownfield/utils/project.d.ts.map +1 -1
- package/dist/brownfield/utils/project.js +47 -0
- package/dist/brownfield/utils/resolvePackagedFrameworkName.d.ts +4 -1
- package/dist/brownfield/utils/resolvePackagedFrameworkName.d.ts.map +1 -1
- package/dist/brownfield/utils/resolvePackagedFrameworkName.js +7 -3
- package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts +20 -0
- package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts.map +1 -1
- package/dist/brownfield/utils/supportsPrebuiltExpo.js +68 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/package.json +6 -6
- package/src/brownfield/commands/packageIos.ts +143 -49
- package/src/brownfield/utils/copyDebugBundleToSimulatorSlice.ts +10 -0
- package/src/brownfield/utils/destinationSdks.ts +122 -0
- package/src/brownfield/utils/emitExpoSupportXcframeworks.ts +48 -4
- package/src/brownfield/utils/expo.ts +38 -1
- package/src/brownfield/utils/project.ts +65 -0
- package/src/brownfield/utils/resolvePackagedFrameworkName.ts +16 -5
- package/src/brownfield/utils/supportsPrebuiltExpo.ts +98 -2
- package/src/index.ts +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @callstack/brownfield-cli
|
|
2
2
|
|
|
3
|
+
## 6.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#487](https://github.com/callstack/react-native-brownfield/pull/487) [`79817d1`](https://github.com/callstack/react-native-brownfield/commit/79817d1a21b0cb28ae63d41e935c57af8e7db39f) Thanks [@KisaneNeko](https://github.com/KisaneNeko)! - Support Expo modules in React Native CLI apps. `brownfield package:*` no longer runs `expo prebuild` on projects with committed `android/` and `ios/` directories, which used to delete the brownfield library module. Prebuild now runs only when the native directory is missing or gitignored. If you commit the generated directories of an Expo app, run `npx expo prebuild` yourself after changing `app.json` or config plugins. `create-react-native-brownfield` now detects Expo and sets up the iOS framework to use the Expo runtime. On a re-run after adding Expo, it also raises an existing framework target to iOS 16.4.
|
|
8
|
+
|
|
9
|
+
`--use-prebuilt-expo` now also turns Expo's precompiled modules on or off for `pod install`, and turns on Swift explicit modules so packaging works in projects that still carry the old `SWIFT_ENABLE_EXPLICIT_MODULES = NO` workaround. It defaults to building Expo from source when the app's React Native version doesn't match the one Expo's precompiled modules were built for.
|
|
10
|
+
|
|
11
|
+
`package:ios` now copies the expo-image dependency XCFrameworks (SDWebImage and libavif) as real files instead of symlinks into `node_modules`, so the package works when moved to another machine.
|
|
12
|
+
|
|
13
|
+
Re-running `create-react-native-brownfield` after `pod install` no longer adds a second Sources build phase to the framework target, which made the next `pod install` fail.
|
|
14
|
+
|
|
15
|
+
The `package:ios` script that `create-react-native-brownfield` adds now passes `--add-spm-package`, so the iOS app adds one local Swift package instead of every XCFramework. Existing scripts aren't changed.
|
|
16
|
+
|
|
17
|
+
- [#485](https://github.com/callstack/react-native-brownfield/pull/485) [`69fe3e3`](https://github.com/callstack/react-native-brownfield/commit/69fe3e3f05a49036cae1f3564a47ef3eed186155) Thanks [@hurali97](https://github.com/hurali97)! - respect `--destination` in `package:ios` when merging XCFrameworks
|
|
18
|
+
|
|
3
19
|
## 6.0.0
|
|
4
20
|
|
|
5
21
|
## 5.3.0
|
|
@@ -3,5 +3,7 @@ import { ExampleUsage } from '../../shared/index.js';
|
|
|
3
3
|
export declare function parseUsePrebuiltRnCoreArgument(value: string | boolean): boolean;
|
|
4
4
|
export declare function parseUsePrebuiltExpoArgument(value: string | boolean): boolean;
|
|
5
5
|
export declare const packageIosCommand: Command;
|
|
6
|
+
/** Whether `--use-prebuilt-expo` was chosen (CLI or brownfield.config.json) rather than inferred. */
|
|
7
|
+
export declare function isUsePrebuiltExpoExplicit(value: boolean | undefined): value is boolean;
|
|
6
8
|
export declare const packageIosExample: ExampleUsage;
|
|
7
9
|
//# 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;AAU5C,OAAO,EAGL,YAAY,EACb,MAAM,uBAAuB,CAAC;AAgD/B,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,MAAM,GAAG,OAAO,GACtB,OAAO,CAET;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAE7E;AAcD,eAAO,MAAM,iBAAiB,SAkX3B,CAAC;AAEJ,qGAAqG;AACrG,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,oBAEnE;AAED,eAAO,MAAM,iBAAiB,cAG7B,CAAC"}
|
|
@@ -6,13 +6,14 @@ import { RockError, colorLink, getReactNativeVersion, logger, relativeToCwd, } f
|
|
|
6
6
|
import { Command, Option } from 'commander';
|
|
7
7
|
import { runExpoPrebuildIfNeeded } from '../utils/expo.js';
|
|
8
8
|
import { findProjectRoot } from '../utils/paths.js';
|
|
9
|
-
import { getProjectInfo } from '../utils/project.js';
|
|
10
|
-
import { supportsPrebuiltExpo } from '../utils/supportsPrebuiltExpo.js';
|
|
9
|
+
import { getProjectInfo, isExpoProject } from '../utils/project.js';
|
|
10
|
+
import { isPrecompiledExpoDisabledByPodfile, supportsPrebuiltExpo, } from '../utils/supportsPrebuiltExpo.js';
|
|
11
11
|
import { supportsPrebuiltRNCore } from '../utils/supportsPrebuiltRNCore.js';
|
|
12
12
|
import { actionRunner, curryOptions, ExampleUsage, } from '../../shared/index.js';
|
|
13
13
|
import { runBrownieCodegenIfApplicable } from '../../brownie/helpers/runBrownieCodegenIfApplicable.js';
|
|
14
14
|
import { runNavigationCodegenIfApplicable } from '../../navigation/helpers/runNavigationCodegenIfApplicable.js';
|
|
15
15
|
import { copyDebugBundleToSimulatorSlice } from '../utils/copyDebugBundleToSimulatorSlice.js';
|
|
16
|
+
import { collectFrameworkPaths, resolveDestinationSdks, } from '../utils/destinationSdks.js';
|
|
16
17
|
import { resolvePackagedFrameworkName } from '../utils/resolvePackagedFrameworkName.js';
|
|
17
18
|
import { stripFrameworkBinary } from '../utils/stripFrameworkBinary.js';
|
|
18
19
|
import { createLocalSpmPackage } from '../utils/createLocalSpmPackage.js';
|
|
@@ -68,6 +69,9 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
68
69
|
.addOption(new Option('--add-spm-package', 'Generate a local Swift Package Manager manifest next to the packaged XCFramework outputs'))
|
|
69
70
|
.action(actionRunner(async (cliOptions) => {
|
|
70
71
|
const options = mergeBrownfieldConfigWithOptions(cliOptions, 'ios');
|
|
72
|
+
// captured before the version-aware default below, so that a value from
|
|
73
|
+
// brownfield.config.json counts as explicit
|
|
74
|
+
const usePrebuiltExpoExplicit = isUsePrebuiltExpoExplicit(options.usePrebuiltExpo);
|
|
71
75
|
const projectRoot = findProjectRoot();
|
|
72
76
|
await runExpoPrebuildIfNeeded({ projectRoot, platform: 'ios' });
|
|
73
77
|
const { platformConfig, userConfig } = getProjectInfo('ios');
|
|
@@ -77,10 +81,46 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
77
81
|
options.usePrebuiltRnCore ??= prebuiltRNCoreSupport.supported
|
|
78
82
|
? prebuiltRNCoreSupport.enabledByDefault
|
|
79
83
|
: false;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
const usePrebuiltExpoWasPassed = options.usePrebuiltExpo !== undefined;
|
|
85
|
+
const precompiledExpoDisabledByPodfile = isExpoProject(projectRoot) &&
|
|
86
|
+
isPrecompiledExpoDisabledByPodfile(projectRoot);
|
|
87
|
+
if (precompiledExpoDisabledByPodfile) {
|
|
88
|
+
if (options.usePrebuiltExpo) {
|
|
89
|
+
logger.warn('ios/Podfile.properties.json sets EXPO_USE_PRECOMPILED_MODULES to "false", so pod install builds Expo modules from source and --use-prebuilt-expo will fail. Remove that property or pass --use-prebuilt-expo false.');
|
|
90
|
+
}
|
|
91
|
+
else if (!usePrebuiltExpoWasPassed) {
|
|
92
|
+
logger.info('ios/Podfile.properties.json turns Expo precompiled modules off. Building Expo modules from source.');
|
|
93
|
+
options.usePrebuiltExpo = false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (!usePrebuiltExpoWasPassed &&
|
|
97
|
+
isExpoProject(projectRoot) &&
|
|
98
|
+
process.env.EXPO_USE_PRECOMPILED_MODULES === '0') {
|
|
99
|
+
// Respect an exported opt-out, the same way Expo's Podfile does.
|
|
100
|
+
logger.info('EXPO_USE_PRECOMPILED_MODULES is set to 0. Building Expo modules from source.');
|
|
101
|
+
options.usePrebuiltExpo = false;
|
|
102
|
+
}
|
|
103
|
+
const usePrebuiltExpoWasChosen = usePrebuiltExpoWasPassed || options.usePrebuiltExpo !== undefined;
|
|
104
|
+
// version-aware default when the flag is omitted; Expo's precompiled
|
|
105
|
+
// modules also need the prebuilt React Native core
|
|
106
|
+
if (options.usePrebuiltExpo === undefined) {
|
|
107
|
+
options.usePrebuiltExpo = prebuiltExpoSupport.supported
|
|
108
|
+
? prebuiltExpoSupport.enabledByDefault && !!options.usePrebuiltRnCore
|
|
109
|
+
: false;
|
|
110
|
+
if (prebuiltExpoSupport.enabledByDefault &&
|
|
111
|
+
!options.usePrebuiltRnCore) {
|
|
112
|
+
logger.info('Expo precompiled modules need the prebuilt React Native core, which is off. Building Expo modules from source.');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (prebuiltExpoSupport.supported &&
|
|
116
|
+
prebuiltExpoSupport.defaultDisabledReason) {
|
|
117
|
+
if (usePrebuiltExpoWasChosen && options.usePrebuiltExpo) {
|
|
118
|
+
logger.warn(`${prebuiltExpoSupport.defaultDisabledReason} The packaged app will likely crash at runtime. Pass --use-prebuilt-expo false to build Expo modules from source.`);
|
|
119
|
+
}
|
|
120
|
+
else if (!usePrebuiltExpoWasChosen) {
|
|
121
|
+
logger.info(`${prebuiltExpoSupport.defaultDisabledReason} Building Expo modules from source.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
84
124
|
if (prebuiltRNCoreSupport) {
|
|
85
125
|
logger.info(`${options.usePrebuiltRnCore ? 'Using' : 'Not using'} prebuilt RN core`);
|
|
86
126
|
if (!prebuiltRNCoreSupport.enabledByDefault &&
|
|
@@ -94,6 +134,25 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
94
134
|
if (options.usePrebuiltExpo && !prebuiltExpoSupport.supported) {
|
|
95
135
|
throw new RockError(prebuiltExpoSupport.reason);
|
|
96
136
|
}
|
|
137
|
+
if (isExpoProject(projectRoot)) {
|
|
138
|
+
// Read by Expo's autolinking during `pod install`. Expo-generated
|
|
139
|
+
// Podfiles default it to on, but a React Native CLI app's Podfile
|
|
140
|
+
// never sets it, so the flag has to drive it.
|
|
141
|
+
process.env.EXPO_USE_PRECOMPILED_MODULES = options.usePrebuiltExpo
|
|
142
|
+
? '1'
|
|
143
|
+
: '0';
|
|
144
|
+
}
|
|
145
|
+
if (options.usePrebuiltExpo &&
|
|
146
|
+
!options.extraParams?.some((param) => param.startsWith('SWIFT_ENABLE_EXPLICIT_MODULES='))) {
|
|
147
|
+
// Precompiled Expo builds the React Native pods as static libraries.
|
|
148
|
+
// With explicit modules off (a leftover older React Native versions
|
|
149
|
+
// wrote into app projects), Xcode cannot find their module maps when
|
|
150
|
+
// verifying the framework's Swift interface.
|
|
151
|
+
options.extraParams = [
|
|
152
|
+
...(options.extraParams ?? []),
|
|
153
|
+
'SWIFT_ENABLE_EXPLICIT_MODULES=YES',
|
|
154
|
+
];
|
|
155
|
+
}
|
|
97
156
|
if (!userConfig.project.ios) {
|
|
98
157
|
throw new Error('iOS project not found.');
|
|
99
158
|
}
|
|
@@ -117,6 +176,9 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
117
176
|
// Reference: https://github.com/facebook/react-native/blob/490c5e8dcc6cdb19c334cc39e93a39a48ba71e96/packages/react-native/scripts/cocoapods/new_architecture.rb#L171
|
|
118
177
|
const packageDir = path.join(dotBrownfieldDir, 'package', 'build');
|
|
119
178
|
const configuration = options.configuration ?? 'Debug';
|
|
179
|
+
// `--destination` narrows which slices Xcode emits, so every path below has to
|
|
180
|
+
// look at those slices only instead of assuming a device + simulator pair
|
|
181
|
+
const sdks = resolveDestinationSdks(options.destination);
|
|
120
182
|
const { hasBrownie } = await runBrownieCodegenIfApplicable(projectRoot, 'swift');
|
|
121
183
|
const { hasNavigation } = await runNavigationCodegenIfApplicable(projectRoot);
|
|
122
184
|
await packageIosAction(options, {
|
|
@@ -134,12 +196,18 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
134
196
|
projectRoot,
|
|
135
197
|
packageDir,
|
|
136
198
|
usePrebuiltExpo: options.usePrebuiltExpo,
|
|
199
|
+
usePrebuiltExpoExplicit,
|
|
200
|
+
// keep the generated SPM manifest consistent with the emitted XCFrameworks
|
|
201
|
+
onDegradeToSource: () => {
|
|
202
|
+
options.usePrebuiltExpo = false;
|
|
203
|
+
},
|
|
137
204
|
});
|
|
138
205
|
const productsPath = path.join(options.buildFolder, 'Build', 'Products');
|
|
139
206
|
const { frameworkName, resolution, candidates } = resolvePackagedFrameworkName({
|
|
140
207
|
explicitScheme: options.scheme,
|
|
141
208
|
productsPath,
|
|
142
209
|
configuration,
|
|
210
|
+
sdks,
|
|
143
211
|
});
|
|
144
212
|
if (!frameworkName && options.addSpmPackage) {
|
|
145
213
|
throw new RockError(`Cannot generate local SPM package: ${getPackagedFrameworkResolutionFailureMessage({
|
|
@@ -152,15 +220,18 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
152
220
|
productsPath,
|
|
153
221
|
configuration,
|
|
154
222
|
frameworkName,
|
|
223
|
+
sdks,
|
|
155
224
|
});
|
|
156
225
|
if (configuration.includes('Debug')) {
|
|
157
226
|
// Re-merge only Debug frameworks so the simulator slice includes main.jsbundle.
|
|
158
227
|
await mergeFrameworks({
|
|
159
228
|
sourceDir: userConfig.project.ios.sourceDir,
|
|
160
|
-
frameworkPaths:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
229
|
+
frameworkPaths: collectFrameworkPaths({
|
|
230
|
+
productsPath,
|
|
231
|
+
configuration,
|
|
232
|
+
sdks,
|
|
233
|
+
frameworkName,
|
|
234
|
+
}),
|
|
164
235
|
outputPath: path.join(packageDir, `${frameworkName}.xcframework`),
|
|
165
236
|
});
|
|
166
237
|
}
|
|
@@ -182,10 +253,13 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
182
253
|
const brownieOutputPath = path.join(packageDir, 'Brownie.xcframework');
|
|
183
254
|
await mergeFrameworks({
|
|
184
255
|
sourceDir: userConfig.project.ios.sourceDir,
|
|
185
|
-
frameworkPaths:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
256
|
+
frameworkPaths: collectFrameworkPaths({
|
|
257
|
+
productsPath,
|
|
258
|
+
configuration,
|
|
259
|
+
sdks,
|
|
260
|
+
frameworkName: 'Brownie',
|
|
261
|
+
productSubDir: 'Brownie',
|
|
262
|
+
}),
|
|
189
263
|
outputPath: brownieOutputPath,
|
|
190
264
|
});
|
|
191
265
|
// Strip the binary from Brownie.xcframework to make it interface-only.
|
|
@@ -198,10 +272,13 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
198
272
|
const brownfieldNavigationOutputPath = path.join(packageDir, 'BrownfieldNavigation.xcframework');
|
|
199
273
|
await mergeFrameworks({
|
|
200
274
|
sourceDir: userConfig.project.ios.sourceDir,
|
|
201
|
-
frameworkPaths:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
275
|
+
frameworkPaths: collectFrameworkPaths({
|
|
276
|
+
productsPath,
|
|
277
|
+
configuration,
|
|
278
|
+
sdks,
|
|
279
|
+
frameworkName: 'BrownfieldNavigation',
|
|
280
|
+
productSubDir: 'BrownfieldNavigation',
|
|
281
|
+
}),
|
|
205
282
|
outputPath: brownfieldNavigationOutputPath,
|
|
206
283
|
});
|
|
207
284
|
stripFrameworkBinary(brownfieldNavigationOutputPath);
|
|
@@ -218,5 +295,12 @@ export const packageIosCommand = curryOptions(new Command('package:ios').descrip
|
|
|
218
295
|
logger.info(`Add the local package folder in Xcode: ${colorLink(relativeToCwd(packageDir))}`);
|
|
219
296
|
logger.info('In Xcode, choose File > Add Package Dependencies..., click Add Local..., and select that folder.');
|
|
220
297
|
}
|
|
298
|
+
// The dependency prints its own "Success" before the post-build steps above,
|
|
299
|
+
// so log a final line marking the true end of the run.
|
|
300
|
+
logger.success(`package:ios finished. Artifacts are in ${colorLink(relativeToCwd(packageDir))}`);
|
|
221
301
|
}));
|
|
302
|
+
/** Whether `--use-prebuilt-expo` was chosen (CLI or brownfield.config.json) rather than inferred. */
|
|
303
|
+
export function isUsePrebuiltExpoExplicit(value) {
|
|
304
|
+
return value !== undefined;
|
|
305
|
+
}
|
|
222
306
|
export const packageIosExample = new ExampleUsage('package:ios --scheme BrownfieldLib --configuration Release', "Build iOS XCFramework for 'BrownfieldLib' scheme in Release configuration");
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { type AppleSdk } from './destinationSdks.js';
|
|
1
2
|
interface CopyDebugBundleToSimulatorSliceOptions {
|
|
2
3
|
productsPath: string;
|
|
3
4
|
configuration: string;
|
|
4
5
|
frameworkName: string;
|
|
6
|
+
/** Slices the build produced; defaults to both. */
|
|
7
|
+
sdks?: AppleSdk[];
|
|
5
8
|
}
|
|
6
|
-
export declare function copyDebugBundleToSimulatorSlice({ productsPath, configuration, frameworkName, }: CopyDebugBundleToSimulatorSliceOptions): void;
|
|
9
|
+
export declare function copyDebugBundleToSimulatorSlice({ productsPath, configuration, frameworkName, sdks, }: CopyDebugBundleToSimulatorSliceOptions): void;
|
|
7
10
|
export {};
|
|
8
11
|
//# sourceMappingURL=copyDebugBundleToSimulatorSlice.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyDebugBundleToSimulatorSlice.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/copyDebugBundleToSimulatorSlice.ts"],"names":[],"mappings":"AAKA,UAAU,sCAAsC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"copyDebugBundleToSimulatorSlice.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/copyDebugBundleToSimulatorSlice.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErE,UAAU,sCAAsC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,YAAY,EACZ,aAAa,EACb,aAAa,EACb,IAAqB,GACtB,EAAE,sCAAsC,QA+CxC"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { colorLink, logger, relativeToCwd } from '@rock-js/tools';
|
|
4
|
-
|
|
4
|
+
import { ALL_APPLE_SDKS } from './destinationSdks.js';
|
|
5
|
+
export function copyDebugBundleToSimulatorSlice({ productsPath, configuration, frameworkName, sdks = ALL_APPLE_SDKS, }) {
|
|
5
6
|
if (!configuration.includes('Debug')) {
|
|
6
7
|
return;
|
|
7
8
|
}
|
|
9
|
+
// nothing to copy across when only one slice was built
|
|
10
|
+
if (!sdks.includes('iphoneos') || !sdks.includes('iphonesimulator')) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
8
13
|
const deviceBundlePath = path.join(productsPath, `${configuration}-iphoneos`, `${frameworkName}.framework`, 'main.jsbundle');
|
|
9
14
|
const simulatorFrameworkPath = path.join(productsPath, `${configuration}-iphonesimulator`, `${frameworkName}.framework`);
|
|
10
15
|
const simulatorBundlePath = path.join(simulatorFrameworkPath, 'main.jsbundle');
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Xcode SDK a build product directory belongs to (`Release-iphoneos`, `Release-iphonesimulator`). */
|
|
2
|
+
export type AppleSdk = 'iphoneos' | 'iphonesimulator';
|
|
3
|
+
/** Both slices, in the order Rock's `packageIosAction` builds them. */
|
|
4
|
+
export declare const ALL_APPLE_SDKS: AppleSdk[];
|
|
5
|
+
/**
|
|
6
|
+
* SDK slices a `package:ios` run produces for the given `--destination` values.
|
|
7
|
+
*
|
|
8
|
+
* Omitting `--destination` builds both slices, matching Rock's default.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveDestinationSdks(destination?: string[] | null): AppleSdk[];
|
|
11
|
+
/**
|
|
12
|
+
* Whether `directoryPath` holds a build product `mergeFrameworks` can consume:
|
|
13
|
+
* either a real `.framework` bundle or the static library it synthesizes one from.
|
|
14
|
+
*/
|
|
15
|
+
export declare function hasFrameworkBuildProduct(directoryPath: string, frameworkName: string): boolean;
|
|
16
|
+
interface CollectFrameworkPathsOptions {
|
|
17
|
+
productsPath: string;
|
|
18
|
+
configuration: string;
|
|
19
|
+
sdks: AppleSdk[];
|
|
20
|
+
frameworkName: string;
|
|
21
|
+
/** Sub-directory of the configuration products dir, e.g. `Brownie` for a Pod target. */
|
|
22
|
+
productSubDir?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `.framework` paths to merge into an XCFramework, limited to the slices that were
|
|
26
|
+
* actually built. Slices with no build product on disk are dropped, so a
|
|
27
|
+
* `--destination simulator` run merges the simulator slice alone instead of failing
|
|
28
|
+
* on a missing `-iphoneos` directory.
|
|
29
|
+
*
|
|
30
|
+
* Throws when no slice has a build product: passing an empty list to
|
|
31
|
+
* `mergeFrameworks` would surface as xcodebuild's opaque
|
|
32
|
+
* "at least one framework or library must be specified".
|
|
33
|
+
*/
|
|
34
|
+
export declare function collectFrameworkPaths({ productsPath, configuration, sdks, frameworkName, productSubDir, }: CollectFrameworkPathsOptions): string[];
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=destinationSdks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destinationSdks.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/destinationSdks.ts"],"names":[],"mappings":"AAKA,sGAAsG;AACtG,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAEtD,uEAAuE;AACvE,eAAO,MAAM,cAAc,EAAE,QAAQ,EAAoC,CAAC;AA4B1E;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAC5B,QAAQ,EAAE,CASZ;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,WAMtB;AAED,UAAU,4BAA4B;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,aAAa,EACb,aAAa,GACd,EAAE,4BAA4B,GAAG,MAAM,EAAE,CA2BzC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { RockError } from '@rock-js/tools';
|
|
4
|
+
/** Both slices, in the order Rock's `packageIosAction` builds them. */
|
|
5
|
+
export const ALL_APPLE_SDKS = ['iphoneos', 'iphonesimulator'];
|
|
6
|
+
/**
|
|
7
|
+
* Map a single `--destination` value onto the SDK(s) its build products land in.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `resolveDestination` in `@rock-js/platform-apple-helpers`: the aliases
|
|
10
|
+
* `device`/`simulator` plus any raw `xcodebuild -destination` string. A value we
|
|
11
|
+
* cannot classify (`id=<udid>`, `name=iPhone 17`) may resolve to either SDK, so it
|
|
12
|
+
* widens to both rather than silently dropping a slice.
|
|
13
|
+
*/
|
|
14
|
+
function resolveSdksForDestination(destination) {
|
|
15
|
+
const normalized = destination.trim().toLowerCase();
|
|
16
|
+
if (normalized === 'device') {
|
|
17
|
+
return ['iphoneos'];
|
|
18
|
+
}
|
|
19
|
+
if (normalized === 'simulator' || normalized.includes('simulator')) {
|
|
20
|
+
return ['iphonesimulator'];
|
|
21
|
+
}
|
|
22
|
+
if (normalized.includes('platform=ios')) {
|
|
23
|
+
return ['iphoneos'];
|
|
24
|
+
}
|
|
25
|
+
return ALL_APPLE_SDKS;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* SDK slices a `package:ios` run produces for the given `--destination` values.
|
|
29
|
+
*
|
|
30
|
+
* Omitting `--destination` builds both slices, matching Rock's default.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveDestinationSdks(destination) {
|
|
33
|
+
if (!destination || destination.length === 0) {
|
|
34
|
+
return ALL_APPLE_SDKS;
|
|
35
|
+
}
|
|
36
|
+
const sdks = new Set(destination.flatMap(resolveSdksForDestination));
|
|
37
|
+
// keep a stable order regardless of the order the flags were passed in
|
|
38
|
+
return ALL_APPLE_SDKS.filter((sdk) => sdks.has(sdk));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether `directoryPath` holds a build product `mergeFrameworks` can consume:
|
|
42
|
+
* either a real `.framework` bundle or the static library it synthesizes one from.
|
|
43
|
+
*/
|
|
44
|
+
export function hasFrameworkBuildProduct(directoryPath, frameworkName) {
|
|
45
|
+
return (fs.existsSync(path.join(directoryPath, `${frameworkName}.framework`)) ||
|
|
46
|
+
fs.existsSync(path.join(directoryPath, `lib${frameworkName}.a`)));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `.framework` paths to merge into an XCFramework, limited to the slices that were
|
|
50
|
+
* actually built. Slices with no build product on disk are dropped, so a
|
|
51
|
+
* `--destination simulator` run merges the simulator slice alone instead of failing
|
|
52
|
+
* on a missing `-iphoneos` directory.
|
|
53
|
+
*
|
|
54
|
+
* Throws when no slice has a build product: passing an empty list to
|
|
55
|
+
* `mergeFrameworks` would surface as xcodebuild's opaque
|
|
56
|
+
* "at least one framework or library must be specified".
|
|
57
|
+
*/
|
|
58
|
+
export function collectFrameworkPaths({ productsPath, configuration, sdks, frameworkName, productSubDir, }) {
|
|
59
|
+
const searchedDirectories = sdks.map((sdk) => path.join(productsPath, `${configuration}-${sdk}`, ...(productSubDir ? [productSubDir] : [])));
|
|
60
|
+
const frameworkPaths = searchedDirectories
|
|
61
|
+
.filter((directoryPath) => hasFrameworkBuildProduct(directoryPath, frameworkName))
|
|
62
|
+
.map((directoryPath) => path.join(directoryPath, `${frameworkName}.framework`));
|
|
63
|
+
if (frameworkPaths.length === 0) {
|
|
64
|
+
throw new RockError(`Could not find a build product for ${frameworkName} in the ${configuration} configuration. ` +
|
|
65
|
+
`Looked for ${frameworkName}.framework or lib${frameworkName}.a in:\n` +
|
|
66
|
+
searchedDirectories.map((dir) => ` - ${dir}`).join('\n') +
|
|
67
|
+
`\nIf the build produced an .app instead of a framework, the wrong scheme was built; pass --scheme with your brownfield framework scheme.`);
|
|
68
|
+
}
|
|
69
|
+
return frameworkPaths;
|
|
70
|
+
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
export declare const EXPO_SUPPORT_XCFRAMEWORK_NAMES: readonly ["ExpoModulesJSI", "ExpoFileSystem", "ExpoFont", "ExpoModulesCore", "ExpoImage", "ExpoModulesWorklets"];
|
|
2
2
|
export declare const ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES: readonly ["ExpoModulesJSI", "ExpoFileSystem", "ExpoFont", "ExpoModulesCore", "ExpoImage", "ExpoModulesWorklets", "SDWebImage", "SDWebImageSVGCoder", "SDWebImageWebPCoder", "SDWebImageAVIFCoder", "libavif"];
|
|
3
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, }: {
|
|
4
|
+
export declare function emitExpoSupportXcframeworks({ projectRoot, packageDir, usePrebuiltExpo, usePrebuiltExpoExplicit, onDegradeToSource, }: {
|
|
5
5
|
projectRoot: string;
|
|
6
6
|
packageDir: string;
|
|
7
7
|
usePrebuiltExpo?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Whether `usePrebuiltExpo` was chosen by the user (CLI flag or brownfield.config.json)
|
|
10
|
+
* rather than inferred from the Expo SDK version. When inferred, missing prebuilts
|
|
11
|
+
* degrade to building Expo modules from source instead of throwing.
|
|
12
|
+
*/
|
|
13
|
+
usePrebuiltExpoExplicit?: boolean;
|
|
14
|
+
/** Called when an inferred `usePrebuiltExpo` was degraded to `false` because prebuilts are absent */
|
|
15
|
+
onDegradeToSource?: () => void;
|
|
8
16
|
}): boolean;
|
|
9
17
|
//# sourceMappingURL=emitExpoSupportXcframeworks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitExpoSupportXcframeworks.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/emitExpoSupportXcframeworks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"emitExpoSupportXcframeworks.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/emitExpoSupportXcframeworks.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,8BAA8B,kHAOjC,CAAC;AAEX,eAAO,MAAM,kCAAkC,+MAGrC,CAAC;AAEX,wBAAgB,8BAA8B,CAAC,eAAe,EAAE,OAAO,+OAItE;AAsDD,wBAAgB,2BAA2B,CAAC,EAC1C,WAAW,EACX,UAAU,EACV,eAAsB,EACtB,uBAA8B,EAC9B,iBAAiB,GAClB,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,qGAAqG;IACrG,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC,WAwDA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { RockError } from '@rock-js/tools';
|
|
3
|
+
import { logger, RockError } from '@rock-js/tools';
|
|
4
4
|
import { normalizeCopiedXcframework } from './normalizeCopiedXcframework.js';
|
|
5
|
-
import { getExpoSdkMajor, isExpoProject } from './project.js';
|
|
5
|
+
import { getExpoIosUsePrecompiledModules, getExpoSdkMajor, isExpoProject, } from './project.js';
|
|
6
6
|
import { MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO } from './supportsPrebuiltExpo.js';
|
|
7
7
|
/**
|
|
8
8
|
* Swift support XCFrameworks
|
|
@@ -47,7 +47,13 @@ function resolveExpoFrameworkSourcePath(projectRoot, frameworkName) {
|
|
|
47
47
|
}
|
|
48
48
|
throw new RockError(`Unsupported Expo XCFramework: ${frameworkName}`);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
function getPrebuiltExpoRemedy(projectRoot) {
|
|
51
|
+
const cause = getExpoIosUsePrecompiledModules(projectRoot) === false
|
|
52
|
+
? 'expo-build-properties sets ios.usePrecompiledModules to false, so prebuilt Expo XCFrameworks are never produced. '
|
|
53
|
+
: '';
|
|
54
|
+
return `${cause}Either re-run with --use-prebuilt-expo false to build Expo modules from source, or run \`pod install\` (with ios.usePrecompiledModules enabled) to materialize the prebuilts.`;
|
|
55
|
+
}
|
|
56
|
+
export function emitExpoSupportXcframeworks({ projectRoot, packageDir, usePrebuiltExpo = true, usePrebuiltExpoExplicit = true, onDegradeToSource, }) {
|
|
51
57
|
if (!isExpoProject(projectRoot)) {
|
|
52
58
|
return false;
|
|
53
59
|
}
|
|
@@ -56,14 +62,30 @@ export function emitExpoSupportXcframeworks({ projectRoot, packageDir, usePrebui
|
|
|
56
62
|
expoSdkMajor < MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO) {
|
|
57
63
|
return false;
|
|
58
64
|
}
|
|
65
|
+
if (usePrebuiltExpo && !usePrebuiltExpoExplicit) {
|
|
66
|
+
const missingPrebuilts = ALL_EXPO_SUPPORT_XCFRAMEWORK_NAMES.filter((frameworkName) => !fs.existsSync(resolveExpoFrameworkSourcePath(projectRoot, frameworkName)));
|
|
67
|
+
// ExpoModulesJSI ships in node_modules and is not a Pods prebuilt; only degrade
|
|
68
|
+
// when Pods-provided prebuilts are absent.
|
|
69
|
+
if (missingPrebuilts.some((name) => name !== 'ExpoModulesJSI')) {
|
|
70
|
+
logger.warn(`Prebuilt Expo XCFrameworks were not found (${missingPrebuilts.join(', ')}). ${getPrebuiltExpoRemedy(projectRoot)} Continuing without prebuilt Expo (--use-prebuilt-expo false); pass --use-prebuilt-expo true to make this an error.`);
|
|
71
|
+
usePrebuiltExpo = false;
|
|
72
|
+
onDegradeToSource?.();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
59
75
|
for (const frameworkName of getExpoSupportXcframeworkNames(usePrebuiltExpo)) {
|
|
60
76
|
const sourcePath = resolveExpoFrameworkSourcePath(projectRoot, frameworkName);
|
|
61
77
|
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)}`);
|
|
78
|
+
throw new RockError(`Expected Expo SDK ${MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO}+ XCFramework not found: ${frameworkName}.xcframework at ${path.relative(projectRoot, sourcePath)}. ${getPrebuiltExpoRemedy(projectRoot)}`);
|
|
63
79
|
}
|
|
64
80
|
const destinationPath = path.join(packageDir, `${frameworkName}.xcframework`);
|
|
65
81
|
fs.rmSync(destinationPath, { recursive: true, force: true });
|
|
66
|
-
|
|
82
|
+
// With precompiled Expo, CocoaPods symlinks expo-image's dependencies into
|
|
83
|
+
// node_modules. Copy what the link points to, so the package is portable
|
|
84
|
+
// and normalizing it doesn't modify node_modules.
|
|
85
|
+
fs.cpSync(fs.realpathSync(sourcePath), destinationPath, {
|
|
86
|
+
recursive: true,
|
|
87
|
+
verbatimSymlinks: true,
|
|
88
|
+
});
|
|
67
89
|
normalizeCopiedXcframework(destinationPath);
|
|
68
90
|
}
|
|
69
91
|
return true;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
type ExpoPlatform = 'ios' | 'android';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the platform's native project is generated by `expo prebuild` (Continuous Native
|
|
4
|
+
* Generation) rather than maintained by hand. A bare React Native app that installed Expo modules
|
|
5
|
+
* commits its native directories; running prebuild there would wipe them (including the
|
|
6
|
+
* brownfield library module), so prebuild only runs when the directory is missing or gitignored.
|
|
7
|
+
*/
|
|
8
|
+
export declare function usesContinuousNativeGeneration(projectRoot: string, platform: ExpoPlatform): boolean;
|
|
2
9
|
export declare function runExpoPrebuildIfNeeded({ projectRoot, platform, }: {
|
|
3
10
|
projectRoot: string;
|
|
4
11
|
platform: ExpoPlatform;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expo.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/expo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"expo.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/expo.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG,KAAK,GAAG,SAAS,CAAC;AAmDtC;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,YAAY,GACrB,OAAO,CAGT;AAED,wBAAsB,uBAAuB,CAAC,EAC5C,WAAW,EACX,QAAQ,GACT,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;CACxB,oBA4BA"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
1
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
2
4
|
import { logger } from '@rock-js/tools';
|
|
3
5
|
import { isExpoProject } from './project.js';
|
|
4
6
|
async function spawnCommand({ command, args, cwd, }) {
|
|
@@ -22,10 +24,35 @@ async function spawnCommand({ command, args, cwd, }) {
|
|
|
22
24
|
function getNpxCommand() {
|
|
23
25
|
return process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
24
26
|
}
|
|
27
|
+
function isGitIgnored(targetPath) {
|
|
28
|
+
// `git check-ignore` exits 0 when ignored, 1 when not, 128 outside a repo.
|
|
29
|
+
const result = spawnSync('git', ['check-ignore', '-q', targetPath], {
|
|
30
|
+
cwd: path.dirname(targetPath),
|
|
31
|
+
stdio: 'ignore',
|
|
32
|
+
});
|
|
33
|
+
if (result.status !== 0 && result.status !== 1) {
|
|
34
|
+
logger.warn(`Couldn't check with git whether ${path.basename(targetPath)}/ is gitignored, so it's treated as committed and expo prebuild is skipped. If this project uses Continuous Native Generation, run \`npx expo prebuild\` before packaging.`);
|
|
35
|
+
}
|
|
36
|
+
return result.status === 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether the platform's native project is generated by `expo prebuild` (Continuous Native
|
|
40
|
+
* Generation) rather than maintained by hand. A bare React Native app that installed Expo modules
|
|
41
|
+
* commits its native directories; running prebuild there would wipe them (including the
|
|
42
|
+
* brownfield library module), so prebuild only runs when the directory is missing or gitignored.
|
|
43
|
+
*/
|
|
44
|
+
export function usesContinuousNativeGeneration(projectRoot, platform) {
|
|
45
|
+
const nativeDir = path.join(projectRoot, platform);
|
|
46
|
+
return !fs.existsSync(nativeDir) || isGitIgnored(nativeDir);
|
|
47
|
+
}
|
|
25
48
|
export async function runExpoPrebuildIfNeeded({ projectRoot, platform, }) {
|
|
26
49
|
if (!isExpoProject(projectRoot)) {
|
|
27
50
|
return false;
|
|
28
51
|
}
|
|
52
|
+
if (!usesContinuousNativeGeneration(projectRoot, platform)) {
|
|
53
|
+
logger.info(`Expo modules detected in a project with a committed ${platform} directory. Skipping expo prebuild.`);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
29
56
|
logger.info(`Expo project detected. Running expo prebuild for ${platform}...`);
|
|
30
57
|
const args = ['expo', 'prebuild', '--platform', platform];
|
|
31
58
|
if (platform === 'ios') {
|
|
@@ -14,6 +14,20 @@ export declare function getExpoConfigIfIsExpo(projectRoot: string): ExpoProjectC
|
|
|
14
14
|
* @returns Whether the project is an Expo project
|
|
15
15
|
*/
|
|
16
16
|
export declare function isExpoProject(projectRoot: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Reads `ios.usePrecompiledModules` from the `expo-build-properties` plugin entry.
|
|
19
|
+
*
|
|
20
|
+
* `getExpoConfigIfIsExpo` uses `skipPlugins: true`, which makes `@expo/config`
|
|
21
|
+
* delete `exp.plugins`, so the plugins array is read from `rootConfig` (the raw
|
|
22
|
+
* static app.json / app.config.json) instead. `rootConfig` is the raw *static*
|
|
23
|
+
* config only, so a dynamic config (app.config.js/ts) exposes no plugins that
|
|
24
|
+
* way; for those we retry once with plugins enabled, which keeps the plugins
|
|
25
|
+
* array. That retry can throw for plugins that are not loadable as Node modules
|
|
26
|
+
* (the reason `skipPlugins` is set in the first place), so it is best-effort.
|
|
27
|
+
*
|
|
28
|
+
* @returns the explicit boolean value, or `undefined` if it is not set or not resolvable
|
|
29
|
+
*/
|
|
30
|
+
export declare function getExpoIosUsePrecompiledModules(projectRoot: string): boolean | undefined;
|
|
17
31
|
export declare function getExpoSdkMajor(projectRoot: string): number | null;
|
|
18
32
|
/**
|
|
19
33
|
* Fills the RNC CLI project config from the Expo config by mutating the passed in `options.projectConfig` object in place
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/project.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,UAAU,EACX,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAKjE,OAAO,EAEL,KAAK,aAAa,IAAI,iBAAiB,EACxC,MAAM,cAAc,CAAC;AAiCtB;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,4BAkBxD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,EAC9C,aAAa,EACb,UAAU,EAAE,EAAE,GAAG,EAAE,EACnB,WAAW,GACZ,EAAE;IACD,8CAA8C;IAC9C,aAAa,EAAE,aAAa,CAAC;IAE7B,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAE9B,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,QAuBA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,SAAS,KAAK,GAAG,SAAS,EAC/D,QAAQ,EAAE,QAAQ,GACjB;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CACzC,CAeA;AAED,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EACX,QAAQ,GACT,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;CAC7B,GAAG,UAAU,CAuBb;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,oBAAoB;;;EAQpC"}
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/project.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,UAAU,EACX,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAKjE,OAAO,EAEL,KAAK,aAAa,IAAI,iBAAiB,EACxC,MAAM,cAAc,CAAC;AAiCtB;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,4BAkBxD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE1D;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,MAAM,GAClB,OAAO,GAAG,SAAS,CA8CrB;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,EAC9C,aAAa,EACb,UAAU,EAAE,EAAE,GAAG,EAAE,EACnB,WAAW,GACZ,EAAE;IACD,8CAA8C;IAC9C,aAAa,EAAE,aAAa,CAAC;IAE7B,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAE9B,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,QAuBA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,SAAS,KAAK,GAAG,SAAS,EAC/D,QAAQ,EAAE,QAAQ,GACjB;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CACzC,CAeA;AAED,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EACX,QAAQ,GACT,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;CAC7B,GAAG,UAAU,CAuBb;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,oBAAoB;;;EAQpC"}
|
|
@@ -53,6 +53,53 @@ export function getExpoConfigIfIsExpo(projectRoot) {
|
|
|
53
53
|
export function isExpoProject(projectRoot) {
|
|
54
54
|
return hasExpoAppConfig(projectRoot) && projectDependsOnExpo(projectRoot);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Reads `ios.usePrecompiledModules` from the `expo-build-properties` plugin entry.
|
|
58
|
+
*
|
|
59
|
+
* `getExpoConfigIfIsExpo` uses `skipPlugins: true`, which makes `@expo/config`
|
|
60
|
+
* delete `exp.plugins`, so the plugins array is read from `rootConfig` (the raw
|
|
61
|
+
* static app.json / app.config.json) instead. `rootConfig` is the raw *static*
|
|
62
|
+
* config only, so a dynamic config (app.config.js/ts) exposes no plugins that
|
|
63
|
+
* way; for those we retry once with plugins enabled, which keeps the plugins
|
|
64
|
+
* array. That retry can throw for plugins that are not loadable as Node modules
|
|
65
|
+
* (the reason `skipPlugins` is set in the first place), so it is best-effort.
|
|
66
|
+
*
|
|
67
|
+
* @returns the explicit boolean value, or `undefined` if it is not set or not resolvable
|
|
68
|
+
*/
|
|
69
|
+
export function getExpoIosUsePrecompiledModules(projectRoot) {
|
|
70
|
+
const config = getExpoConfigIfIsExpo(projectRoot);
|
|
71
|
+
if (!config) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const rootConfig = config.rootConfig;
|
|
75
|
+
let plugins = config.exp.plugins ??
|
|
76
|
+
rootConfig?.expo?.plugins ??
|
|
77
|
+
rootConfig?.plugins;
|
|
78
|
+
if (!Array.isArray(plugins)) {
|
|
79
|
+
// Dynamic config: no static plugins array exists. Retry with plugins
|
|
80
|
+
// enabled, which preserves `exp.plugins`. Best-effort — plugin resolution
|
|
81
|
+
// can throw, and an unreadable config is treated the same as an unset value.
|
|
82
|
+
try {
|
|
83
|
+
plugins = getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp.plugins;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!Array.isArray(plugins)) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
for (const plugin of plugins) {
|
|
93
|
+
if (!Array.isArray(plugin) || plugin[0] !== 'expo-build-properties') {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const value = plugin[1]?.ios?.usePrecompiledModules;
|
|
97
|
+
if (typeof value === 'boolean') {
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
56
103
|
export function getExpoSdkMajor(projectRoot) {
|
|
57
104
|
const rawExpoVersion = getExpoConfigIfIsExpo(projectRoot)?.exp.sdkVersion;
|
|
58
105
|
if (!rawExpoVersion) {
|