@callstack/brownfield-cli 6.0.0 → 6.1.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/brownfield/commands/packageIos.d.ts +2 -0
  3. package/dist/brownfield/commands/packageIos.d.ts.map +1 -1
  4. package/dist/brownfield/commands/packageIos.js +102 -18
  5. package/dist/brownfield/utils/copyDebugBundleToSimulatorSlice.d.ts +4 -1
  6. package/dist/brownfield/utils/copyDebugBundleToSimulatorSlice.d.ts.map +1 -1
  7. package/dist/brownfield/utils/copyDebugBundleToSimulatorSlice.js +6 -1
  8. package/dist/brownfield/utils/destinationSdks.d.ts +36 -0
  9. package/dist/brownfield/utils/destinationSdks.d.ts.map +1 -0
  10. package/dist/brownfield/utils/destinationSdks.js +70 -0
  11. package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts +9 -1
  12. package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts.map +1 -1
  13. package/dist/brownfield/utils/emitExpoSupportXcframeworks.js +27 -5
  14. package/dist/brownfield/utils/expo.d.ts +7 -0
  15. package/dist/brownfield/utils/expo.d.ts.map +1 -1
  16. package/dist/brownfield/utils/expo.js +28 -1
  17. package/dist/brownfield/utils/project.d.ts +14 -0
  18. package/dist/brownfield/utils/project.d.ts.map +1 -1
  19. package/dist/brownfield/utils/project.js +47 -0
  20. package/dist/brownfield/utils/resolvePackagedFrameworkName.d.ts +4 -1
  21. package/dist/brownfield/utils/resolvePackagedFrameworkName.d.ts.map +1 -1
  22. package/dist/brownfield/utils/resolvePackagedFrameworkName.js +7 -3
  23. package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts +20 -0
  24. package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts.map +1 -1
  25. package/dist/brownfield/utils/supportsPrebuiltExpo.js +68 -1
  26. package/dist/expoPluginConfig.d.ts +2 -0
  27. package/dist/expoPluginConfig.d.ts.map +1 -1
  28. package/dist/expoPluginConfig.js +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +10 -0
  31. package/dist/types.d.ts +6 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/package.json +6 -6
  34. package/schema.json +7 -0
  35. package/src/brownfield/commands/packageIos.ts +143 -49
  36. package/src/brownfield/utils/copyDebugBundleToSimulatorSlice.ts +10 -0
  37. package/src/brownfield/utils/destinationSdks.ts +122 -0
  38. package/src/brownfield/utils/emitExpoSupportXcframeworks.ts +48 -4
  39. package/src/brownfield/utils/expo.ts +38 -1
  40. package/src/brownfield/utils/project.ts +65 -0
  41. package/src/brownfield/utils/resolvePackagedFrameworkName.ts +16 -5
  42. package/src/brownfield/utils/supportsPrebuiltExpo.ts +98 -2
  43. package/src/expoPluginConfig.ts +4 -0
  44. package/src/index.ts +12 -0
  45. package/src/types.ts +8 -1
@@ -1,3 +1,4 @@
1
+ import { type AppleSdk } from './destinationSdks.js';
1
2
  type Resolution = 'explicit' | 'detected' | 'not_found' | 'ambiguous';
2
3
  export interface ResolvePackagedFrameworkNameResult {
3
4
  frameworkName: string | null;
@@ -8,7 +9,9 @@ interface ResolvePackagedFrameworkNameOptions {
8
9
  explicitScheme?: string;
9
10
  productsPath: string;
10
11
  configuration: string;
12
+ /** Slices the build produced; defaults to both. */
13
+ sdks?: AppleSdk[];
11
14
  }
12
- export declare function resolvePackagedFrameworkName({ explicitScheme, productsPath, configuration, }: ResolvePackagedFrameworkNameOptions): ResolvePackagedFrameworkNameResult;
15
+ export declare function resolvePackagedFrameworkName({ explicitScheme, productsPath, configuration, sdks, }: ResolvePackagedFrameworkNameOptions): ResolvePackagedFrameworkNameResult;
13
16
  export {};
14
17
  //# sourceMappingURL=resolvePackagedFrameworkName.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolvePackagedFrameworkName.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/resolvePackagedFrameworkName.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;AAEtE,MAAM,WAAW,kCAAkC;IACjD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,UAAU,mCAAmC;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AA4CD,wBAAgB,4BAA4B,CAAC,EAC3C,cAAc,EACd,YAAY,EACZ,aAAa,GACd,EAAE,mCAAmC,GAAG,kCAAkC,CAkC1E"}
1
+ {"version":3,"file":"resolvePackagedFrameworkName.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/resolvePackagedFrameworkName.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErE,KAAK,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;AAEtE,MAAM,WAAW,kCAAkC;IACjD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,UAAU,mCAAmC;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;CACnB;AA4CD,wBAAgB,4BAA4B,CAAC,EAC3C,cAAc,EACd,YAAY,EACZ,aAAa,EACb,IAAqB,GACtB,EAAE,mCAAmC,GAAG,kCAAkC,CAwC1E"}
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { ALL_APPLE_SDKS } from './destinationSdks.js';
3
4
  function collectFrameworkCandidates(configurationProductsPath) {
4
5
  if (!fs.existsSync(configurationProductsPath)) {
5
6
  return [];
@@ -31,15 +32,18 @@ function collectFrameworkCandidates(configurationProductsPath) {
31
32
  }
32
33
  return [...discoveredFrameworks].sort();
33
34
  }
34
- export function resolvePackagedFrameworkName({ explicitScheme, productsPath, configuration, }) {
35
+ export function resolvePackagedFrameworkName({ explicitScheme, productsPath, configuration, sdks = ALL_APPLE_SDKS, }) {
35
36
  if (explicitScheme) {
36
37
  return {
37
38
  frameworkName: explicitScheme,
38
39
  resolution: 'explicit',
39
40
  };
40
41
  }
41
- const configurationProductsPath = path.join(productsPath, `${configuration}-iphoneos`);
42
- const candidates = collectFrameworkCandidates(configurationProductsPath);
42
+ // the JS bundle is emitted per slice, so scan every slice that was built rather
43
+ // than assuming the device one exists (`--destination simulator` skips it)
44
+ const candidates = [
45
+ ...new Set(sdks.flatMap((sdk) => collectFrameworkCandidates(path.join(productsPath, `${configuration}-${sdk}`)))),
46
+ ].sort();
43
47
  if (candidates.length === 1) {
44
48
  return {
45
49
  frameworkName: candidates[0] ?? null,
@@ -2,13 +2,33 @@ export declare const MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO = 56;
2
2
  export type PrebuiltExpoSupportResult = {
3
3
  supported: true;
4
4
  enabledByDefault: boolean;
5
+ /** Why prebuilts are off by default even though they are supported. */
6
+ defaultDisabledReason?: string;
5
7
  reason?: never;
6
8
  } | {
7
9
  supported: false;
8
10
  enabledByDefault?: never;
9
11
  reason: string;
10
12
  };
13
+ /**
14
+ * The React Native version the installed Expo SDK pins. Expo ships its
15
+ * precompiled modules built against this version.
16
+ */
17
+ export declare function getExpoReactNativeVersion(projectRoot: string): string | null;
18
+ /**
19
+ * Describes a React Native version the Expo prebuilts were not built for, or
20
+ * returns null when they match. Prebuilt Expo modules link against React
21
+ * Native's C++ API, which changes between minor versions, so a mismatch
22
+ * crashes at runtime.
23
+ */
24
+ export declare function getPrebuiltExpoReactNativeMismatch(projectRoot: string): string | null;
11
25
  export declare function supportsPrebuiltExpo({ projectRoot, }: {
12
26
  projectRoot: string;
13
27
  }): PrebuiltExpoSupportResult;
28
+ /**
29
+ * Whether the project's Podfile turns Expo's precompiled modules off. Expo
30
+ * Podfiles force `EXPO_USE_PRECOMPILED_MODULES` to '0' when
31
+ * `ios/Podfile.properties.json` sets it to "false", whatever the environment says.
32
+ */
33
+ export declare function isPrecompiledExpoDisabledByPodfile(projectRoot: string): boolean;
14
34
  //# sourceMappingURL=supportsPrebuiltExpo.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"supportsPrebuiltExpo.d.ts","sourceRoot":"","sources":["../../../src/brownfield/utils/supportsPrebuiltExpo.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,MAAM,MAAM,yBAAyB,GACjC;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB,GACD;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMnE;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc5E;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CAYf;AAED,wBAAgB,oBAAoB,CAAC,EACnC,WAAW,GACZ,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,yBAAyB,CAsC5B;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,MAAM,GAClB,OAAO,CAYT"}
@@ -1,5 +1,44 @@
1
- import { getExpoSdkMajor, isExpoProject } from './project.js';
1
+ import fs from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import path from 'node:path';
4
+ import { getReactNativeVersion } from '@rock-js/tools';
5
+ import { getExpoIosUsePrecompiledModules, getExpoSdkMajor, isExpoProject } from './project.js';
2
6
  export const MIN_EXPO_SDK_MAJOR_FOR_PREBUILT_EXPO = 56;
7
+ function majorMinor(version) {
8
+ return version.split('.').slice(0, 2).join('.');
9
+ }
10
+ /**
11
+ * The React Native version the installed Expo SDK pins. Expo ships its
12
+ * precompiled modules built against this version.
13
+ */
14
+ export function getExpoReactNativeVersion(projectRoot) {
15
+ try {
16
+ const require = createRequire(path.join(projectRoot, 'package.json'));
17
+ const expoPackageJson = require.resolve('expo/package.json');
18
+ const bundledNativeModules = JSON.parse(fs.readFileSync(path.join(path.dirname(expoPackageJson), 'bundledNativeModules.json'), 'utf8'));
19
+ return bundledNativeModules['react-native'] ?? null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ /**
26
+ * Describes a React Native version the Expo prebuilts were not built for, or
27
+ * returns null when they match. Prebuilt Expo modules link against React
28
+ * Native's C++ API, which changes between minor versions, so a mismatch
29
+ * crashes at runtime.
30
+ */
31
+ export function getPrebuiltExpoReactNativeMismatch(projectRoot) {
32
+ const expected = getExpoReactNativeVersion(projectRoot);
33
+ const found = getReactNativeVersion(projectRoot);
34
+ if (!expected || found === 'unknown') {
35
+ return null;
36
+ }
37
+ if (majorMinor(expected) === majorMinor(found)) {
38
+ return null;
39
+ }
40
+ return `Expo's precompiled modules are built for React Native ${majorMinor(expected)}, but this project uses ${found}.`;
41
+ }
3
42
  export function supportsPrebuiltExpo({ projectRoot, }) {
4
43
  if (!isExpoProject(projectRoot)) {
5
44
  return { supported: true, enabledByDefault: false };
@@ -13,8 +52,36 @@ export function supportsPrebuiltExpo({ projectRoot, }) {
13
52
  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
53
  };
15
54
  }
55
+ const reactNativeMismatch = getPrebuiltExpoReactNativeMismatch(projectRoot);
56
+ if (reactNativeMismatch) {
57
+ return {
58
+ supported: true,
59
+ enabledByDefault: false,
60
+ defaultDisabledReason: reactNativeMismatch,
61
+ };
62
+ }
63
+ if (getExpoIosUsePrecompiledModules(projectRoot) === false) {
64
+ return {
65
+ supported: false,
66
+ reason: "--use-prebuilt-expo is unsupported: expo-build-properties sets ios.usePrecompiledModules to false in your Expo config, so prebuilt Expo XCFrameworks are never produced. Set it to true (or remove it) and run 'pod install', or pass --use-prebuilt-expo false to build Expo modules from source.",
67
+ };
68
+ }
16
69
  return {
17
70
  supported: true,
18
71
  enabledByDefault: true,
19
72
  };
20
73
  }
74
+ /**
75
+ * Whether the project's Podfile turns Expo's precompiled modules off. Expo
76
+ * Podfiles force `EXPO_USE_PRECOMPILED_MODULES` to '0' when
77
+ * `ios/Podfile.properties.json` sets it to "false", whatever the environment says.
78
+ */
79
+ export function isPrecompiledExpoDisabledByPodfile(projectRoot) {
80
+ try {
81
+ const properties = JSON.parse(fs.readFileSync(path.join(projectRoot, 'ios', 'Podfile.properties.json'), 'utf8'));
82
+ return properties.EXPO_USE_PRECOMPILED_MODULES === 'false';
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
@@ -26,6 +26,7 @@ export type BrownfieldPluginProps = {
26
26
  extraProguardRules?: string[];
27
27
  useLocalMaven?: boolean;
28
28
  missingDimensionStrategies?: string[];
29
+ excludeDependencies?: string[];
29
30
  };
30
31
  };
31
32
  export type ResolvedBrownfieldPluginAndroidConfig = {
@@ -42,6 +43,7 @@ export type ResolvedBrownfieldPluginAndroidConfig = {
42
43
  extraProguardRules: string[];
43
44
  useLocalMaven: boolean;
44
45
  missingDimensionStrategies: string[];
46
+ excludeDependencies: string[];
45
47
  };
46
48
  export type ResolvedBrownfieldPluginIosConfig = {
47
49
  frameworkName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"expoPluginConfig.d.ts","sourceRoot":"","sources":["../src/expoPluginConfig.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"expoPluginConfig.d.ts","sourceRoot":"","sources":["../src/expoPluginConfig.ts"],"names":[],"mappings":"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"}
@@ -107,6 +107,7 @@ export function resolveBrownfieldPluginConfig(pluginProps, fileConfig, expoConfi
107
107
  extraProguardRules,
108
108
  useLocalMaven: effectiveProps.android?.useLocalMaven ?? false,
109
109
  missingDimensionStrategies: effectiveProps.android?.missingDimensionStrategies ?? [],
110
+ excludeDependencies: effectiveProps.android?.excludeDependencies ?? [],
110
111
  ...(effectiveProps.android?.targetSdkVersion != null
111
112
  ? { targetSdkVersion: effectiveProps.android.targetSdkVersion }
112
113
  : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiFA,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAM3C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6FA,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAM3C"}
package/dist/index.js CHANGED
@@ -5,6 +5,16 @@ import { ExampleUsage } from './shared/index.js';
5
5
  import brownfieldCommands, { groupName as brownfieldCommandsGroupName, } from './brownfield/index.js';
6
6
  import brownieCommands, { groupName as brownieCommandsGroupName, } from './brownie/index.js';
7
7
  import navigationCommands, { groupName as navigationCommandsGroupName, } from './navigation/index.js';
8
+ // CocoaPods (spawned by Rock) raises Encoding::CompatibilityError under a non-UTF-8
9
+ // locale. Ruby resolves the character encoding as LC_ALL > LC_CTYPE > LANG, so check
10
+ // them in that precedence. Default to UTF-8 only when the effective value is missing
11
+ // or non-UTF-8.
12
+ const inheritedCharset = process.env.LC_ALL || process.env.LC_CTYPE || process.env.LANG || '';
13
+ if (!/utf-?8/i.test(inheritedCharset)) {
14
+ process.env.LANG = 'en_US.UTF-8';
15
+ process.env.LC_CTYPE = 'en_US.UTF-8';
16
+ process.env.LC_ALL = 'en_US.UTF-8';
17
+ }
8
18
  const program = new Command();
9
19
  program
10
20
  .name(styleText('magenta', 'brownfield'))
package/dist/types.d.ts CHANGED
@@ -90,6 +90,12 @@ export type BrownfieldExpoAndroidConfig = {
90
90
  * Missing dimension strategies as dimension + flavor(s), e.g. "type, alpha".
91
91
  */
92
92
  missingDimensionStrategies?: string[];
93
+ /**
94
+ * Dependencies to keep out of the published AAR's POM and module.json, as
95
+ * "group:module" or "group" for a whole group. Written as Gradle excludes
96
+ * into the generated module, e.g. "com.caverock:androidsvg".
97
+ */
98
+ excludeDependencies?: string[];
93
99
  };
94
100
  /**
95
101
  * Expo config plugin options for iOS prebuild scaffolding.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@callstack/brownfield-cli",
3
- "version": "6.0.0",
3
+ "version": "6.1.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.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",
91
+ "@rock-js/platform-android": "^0.15.4",
92
+ "@rock-js/platform-apple-helpers": "^0.15.4",
93
+ "@rock-js/plugin-brownfield-android": "^0.15.4",
94
+ "@rock-js/plugin-brownfield-ios": "^0.15.4",
95
+ "@rock-js/tools": "^0.15.4",
96
96
  "ajv": "^8.20.0",
97
97
  "commander": "^14.0.3",
98
98
  "quicktype-core": "^23.2.6",
package/schema.json CHANGED
@@ -57,6 +57,13 @@
57
57
  "description": "Compile SDK version for the Android library. Defaults to 37 on Expo SDK 58 and later, otherwise 35.",
58
58
  "type": "number"
59
59
  },
60
+ "excludeDependencies": {
61
+ "description": "Dependencies to keep out of the published AAR's POM and module.json, as \"group:module\" or \"group\" for a whole group. Written as Gradle excludes into the generated module, e.g. \"com.caverock:androidsvg\".",
62
+ "items": {
63
+ "type": "string"
64
+ },
65
+ "type": "array"
66
+ },
60
67
  "extraProguardRules": {
61
68
  "description": "Extra Proguard rules appended to the default rules.",
62
69
  "items": {
@@ -18,8 +18,11 @@ import { Command, Option } from 'commander';
18
18
 
19
19
  import { runExpoPrebuildIfNeeded } from '../utils/expo.js';
20
20
  import { findProjectRoot } from '../utils/paths.js';
21
- import { getProjectInfo } from '../utils/project.js';
22
- import { supportsPrebuiltExpo } from '../utils/supportsPrebuiltExpo.js';
21
+ import { getProjectInfo, isExpoProject } from '../utils/project.js';
22
+ import {
23
+ isPrecompiledExpoDisabledByPodfile,
24
+ supportsPrebuiltExpo,
25
+ } from '../utils/supportsPrebuiltExpo.js';
23
26
  import { supportsPrebuiltRNCore } from '../utils/supportsPrebuiltRNCore.js';
24
27
  import {
25
28
  actionRunner,
@@ -29,6 +32,10 @@ import {
29
32
  import { runBrownieCodegenIfApplicable } from '../../brownie/helpers/runBrownieCodegenIfApplicable.js';
30
33
  import { runNavigationCodegenIfApplicable } from '../../navigation/helpers/runNavigationCodegenIfApplicable.js';
31
34
  import { copyDebugBundleToSimulatorSlice } from '../utils/copyDebugBundleToSimulatorSlice.js';
35
+ import {
36
+ collectFrameworkPaths,
37
+ resolveDestinationSdks,
38
+ } from '../utils/destinationSdks.js';
32
39
  import { resolvePackagedFrameworkName } from '../utils/resolvePackagedFrameworkName.js';
33
40
  import { stripFrameworkBinary } from '../utils/stripFrameworkBinary.js';
34
41
  import type { PackageIosOptions } from '../../types.js';
@@ -75,9 +82,7 @@ export function parseUsePrebuiltRnCoreArgument(
75
82
  return parseBooleanOptionArgument('--use-prebuilt-rn-core', value);
76
83
  }
77
84
 
78
- export function parseUsePrebuiltExpoArgument(
79
- value: string | boolean
80
- ): boolean {
85
+ export function parseUsePrebuiltExpoArgument(value: string | boolean): boolean {
81
86
  return parseBooleanOptionArgument('--use-prebuilt-expo', value);
82
87
  }
83
88
 
@@ -125,6 +130,11 @@ export const packageIosCommand = curryOptions(
125
130
  .action(
126
131
  actionRunner(async (cliOptions: PackageIosOptions) => {
127
132
  const options = mergeBrownfieldConfigWithOptions(cliOptions, 'ios');
133
+ // captured before the version-aware default below, so that a value from
134
+ // brownfield.config.json counts as explicit
135
+ const usePrebuiltExpoExplicit = isUsePrebuiltExpoExplicit(
136
+ options.usePrebuiltExpo
137
+ );
128
138
  const projectRoot = findProjectRoot();
129
139
 
130
140
  await runExpoPrebuildIfNeeded({ projectRoot, platform: 'ios' });
@@ -139,10 +149,68 @@ export const packageIosCommand = curryOptions(
139
149
  ? prebuiltRNCoreSupport.enabledByDefault
140
150
  : false;
141
151
 
142
- // version-aware default when the flag is omitted
143
- options.usePrebuiltExpo ??= prebuiltExpoSupport.supported
144
- ? prebuiltExpoSupport.enabledByDefault
145
- : false;
152
+ const usePrebuiltExpoWasPassed = options.usePrebuiltExpo !== undefined;
153
+ const precompiledExpoDisabledByPodfile =
154
+ isExpoProject(projectRoot) &&
155
+ isPrecompiledExpoDisabledByPodfile(projectRoot);
156
+
157
+ if (precompiledExpoDisabledByPodfile) {
158
+ if (options.usePrebuiltExpo) {
159
+ logger.warn(
160
+ '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.'
161
+ );
162
+ } else if (!usePrebuiltExpoWasPassed) {
163
+ logger.info(
164
+ 'ios/Podfile.properties.json turns Expo precompiled modules off. Building Expo modules from source.'
165
+ );
166
+ options.usePrebuiltExpo = false;
167
+ }
168
+ } else if (
169
+ !usePrebuiltExpoWasPassed &&
170
+ isExpoProject(projectRoot) &&
171
+ process.env.EXPO_USE_PRECOMPILED_MODULES === '0'
172
+ ) {
173
+ // Respect an exported opt-out, the same way Expo's Podfile does.
174
+ logger.info(
175
+ 'EXPO_USE_PRECOMPILED_MODULES is set to 0. Building Expo modules from source.'
176
+ );
177
+ options.usePrebuiltExpo = false;
178
+ }
179
+
180
+ const usePrebuiltExpoWasChosen =
181
+ usePrebuiltExpoWasPassed || options.usePrebuiltExpo !== undefined;
182
+
183
+ // version-aware default when the flag is omitted; Expo's precompiled
184
+ // modules also need the prebuilt React Native core
185
+ if (options.usePrebuiltExpo === undefined) {
186
+ options.usePrebuiltExpo = prebuiltExpoSupport.supported
187
+ ? prebuiltExpoSupport.enabledByDefault && !!options.usePrebuiltRnCore
188
+ : false;
189
+
190
+ if (
191
+ prebuiltExpoSupport.enabledByDefault &&
192
+ !options.usePrebuiltRnCore
193
+ ) {
194
+ logger.info(
195
+ 'Expo precompiled modules need the prebuilt React Native core, which is off. Building Expo modules from source.'
196
+ );
197
+ }
198
+ }
199
+
200
+ if (
201
+ prebuiltExpoSupport.supported &&
202
+ prebuiltExpoSupport.defaultDisabledReason
203
+ ) {
204
+ if (usePrebuiltExpoWasChosen && options.usePrebuiltExpo) {
205
+ logger.warn(
206
+ `${prebuiltExpoSupport.defaultDisabledReason} The packaged app will likely crash at runtime. Pass --use-prebuilt-expo false to build Expo modules from source.`
207
+ );
208
+ } else if (!usePrebuiltExpoWasChosen) {
209
+ logger.info(
210
+ `${prebuiltExpoSupport.defaultDisabledReason} Building Expo modules from source.`
211
+ );
212
+ }
213
+ }
146
214
 
147
215
  if (prebuiltRNCoreSupport) {
148
216
  logger.info(
@@ -167,6 +235,31 @@ export const packageIosCommand = curryOptions(
167
235
  throw new RockError(prebuiltExpoSupport.reason);
168
236
  }
169
237
 
238
+ if (isExpoProject(projectRoot)) {
239
+ // Read by Expo's autolinking during `pod install`. Expo-generated
240
+ // Podfiles default it to on, but a React Native CLI app's Podfile
241
+ // never sets it, so the flag has to drive it.
242
+ process.env.EXPO_USE_PRECOMPILED_MODULES = options.usePrebuiltExpo
243
+ ? '1'
244
+ : '0';
245
+ }
246
+
247
+ if (
248
+ options.usePrebuiltExpo &&
249
+ !options.extraParams?.some((param) =>
250
+ param.startsWith('SWIFT_ENABLE_EXPLICIT_MODULES=')
251
+ )
252
+ ) {
253
+ // Precompiled Expo builds the React Native pods as static libraries.
254
+ // With explicit modules off (a leftover older React Native versions
255
+ // wrote into app projects), Xcode cannot find their module maps when
256
+ // verifying the framework's Swift interface.
257
+ options.extraParams = [
258
+ ...(options.extraParams ?? []),
259
+ 'SWIFT_ENABLE_EXPLICIT_MODULES=YES',
260
+ ];
261
+ }
262
+
170
263
  if (!userConfig.project.ios) {
171
264
  throw new Error('iOS project not found.');
172
265
  }
@@ -197,6 +290,9 @@ export const packageIosCommand = curryOptions(
197
290
  // Reference: https://github.com/facebook/react-native/blob/490c5e8dcc6cdb19c334cc39e93a39a48ba71e96/packages/react-native/scripts/cocoapods/new_architecture.rb#L171
198
291
  const packageDir = path.join(dotBrownfieldDir, 'package', 'build');
199
292
  const configuration = options.configuration ?? 'Debug';
293
+ // `--destination` narrows which slices Xcode emits, so every path below has to
294
+ // look at those slices only instead of assuming a device + simulator pair
295
+ const sdks = resolveDestinationSdks(options.destination);
200
296
 
201
297
  const { hasBrownie } = await runBrownieCodegenIfApplicable(
202
298
  projectRoot,
@@ -225,6 +321,11 @@ export const packageIosCommand = curryOptions(
225
321
  projectRoot,
226
322
  packageDir,
227
323
  usePrebuiltExpo: options.usePrebuiltExpo,
324
+ usePrebuiltExpoExplicit,
325
+ // keep the generated SPM manifest consistent with the emitted XCFrameworks
326
+ onDegradeToSource: () => {
327
+ options.usePrebuiltExpo = false;
328
+ },
228
329
  });
229
330
 
230
331
  const productsPath = path.join(options.buildFolder, 'Build', 'Products');
@@ -233,6 +334,7 @@ export const packageIosCommand = curryOptions(
233
334
  explicitScheme: options.scheme,
234
335
  productsPath,
235
336
  configuration,
337
+ sdks,
236
338
  });
237
339
 
238
340
  if (!frameworkName && options.addSpmPackage) {
@@ -251,24 +353,19 @@ export const packageIosCommand = curryOptions(
251
353
  productsPath,
252
354
  configuration,
253
355
  frameworkName,
356
+ sdks,
254
357
  });
255
358
 
256
359
  if (configuration.includes('Debug')) {
257
360
  // Re-merge only Debug frameworks so the simulator slice includes main.jsbundle.
258
361
  await mergeFrameworks({
259
362
  sourceDir: userConfig.project.ios.sourceDir,
260
- frameworkPaths: [
261
- path.join(
262
- productsPath,
263
- `${configuration}-iphoneos`,
264
- `${frameworkName}.framework`
265
- ),
266
- path.join(
267
- productsPath,
268
- `${configuration}-iphonesimulator`,
269
- `${frameworkName}.framework`
270
- ),
271
- ],
363
+ frameworkPaths: collectFrameworkPaths({
364
+ productsPath,
365
+ configuration,
366
+ sdks,
367
+ frameworkName,
368
+ }),
272
369
  outputPath: path.join(packageDir, `${frameworkName}.xcframework`),
273
370
  });
274
371
  }
@@ -300,20 +397,13 @@ export const packageIosCommand = curryOptions(
300
397
 
301
398
  await mergeFrameworks({
302
399
  sourceDir: userConfig.project.ios.sourceDir,
303
- frameworkPaths: [
304
- path.join(
305
- productsPath,
306
- `${configuration}-iphoneos`,
307
- 'Brownie',
308
- 'Brownie.framework'
309
- ),
310
- path.join(
311
- productsPath,
312
- `${configuration}-iphonesimulator`,
313
- 'Brownie',
314
- 'Brownie.framework'
315
- ),
316
- ],
400
+ frameworkPaths: collectFrameworkPaths({
401
+ productsPath,
402
+ configuration,
403
+ sdks,
404
+ frameworkName: 'Brownie',
405
+ productSubDir: 'Brownie',
406
+ }),
317
407
  outputPath: brownieOutputPath,
318
408
  });
319
409
 
@@ -335,20 +425,13 @@ export const packageIosCommand = curryOptions(
335
425
 
336
426
  await mergeFrameworks({
337
427
  sourceDir: userConfig.project.ios.sourceDir,
338
- frameworkPaths: [
339
- path.join(
340
- productsPath,
341
- `${configuration}-iphoneos`,
342
- 'BrownfieldNavigation',
343
- 'BrownfieldNavigation.framework'
344
- ),
345
- path.join(
346
- productsPath,
347
- `${configuration}-iphonesimulator`,
348
- 'BrownfieldNavigation',
349
- 'BrownfieldNavigation.framework'
350
- ),
351
- ],
428
+ frameworkPaths: collectFrameworkPaths({
429
+ productsPath,
430
+ configuration,
431
+ sdks,
432
+ frameworkName: 'BrownfieldNavigation',
433
+ productSubDir: 'BrownfieldNavigation',
434
+ }),
352
435
  outputPath: brownfieldNavigationOutputPath,
353
436
  });
354
437
 
@@ -378,9 +461,20 @@ export const packageIosCommand = curryOptions(
378
461
  'In Xcode, choose File > Add Package Dependencies..., click Add Local..., and select that folder.'
379
462
  );
380
463
  }
464
+
465
+ // The dependency prints its own "Success" before the post-build steps above,
466
+ // so log a final line marking the true end of the run.
467
+ logger.success(
468
+ `package:ios finished. Artifacts are in ${colorLink(relativeToCwd(packageDir))}`
469
+ );
381
470
  })
382
471
  );
383
472
 
473
+ /** Whether `--use-prebuilt-expo` was chosen (CLI or brownfield.config.json) rather than inferred. */
474
+ export function isUsePrebuiltExpoExplicit(value: boolean | undefined) {
475
+ return value !== undefined;
476
+ }
477
+
384
478
  export const packageIosExample = new ExampleUsage(
385
479
  'package:ios --scheme BrownfieldLib --configuration Release',
386
480
  "Build iOS XCFramework for 'BrownfieldLib' scheme in Release configuration"
@@ -3,21 +3,31 @@ import path from 'node:path';
3
3
 
4
4
  import { colorLink, logger, relativeToCwd } from '@rock-js/tools';
5
5
 
6
+ import { ALL_APPLE_SDKS, type AppleSdk } from './destinationSdks.js';
7
+
6
8
  interface CopyDebugBundleToSimulatorSliceOptions {
7
9
  productsPath: string;
8
10
  configuration: string;
9
11
  frameworkName: string;
12
+ /** Slices the build produced; defaults to both. */
13
+ sdks?: AppleSdk[];
10
14
  }
11
15
 
12
16
  export function copyDebugBundleToSimulatorSlice({
13
17
  productsPath,
14
18
  configuration,
15
19
  frameworkName,
20
+ sdks = ALL_APPLE_SDKS,
16
21
  }: CopyDebugBundleToSimulatorSliceOptions) {
17
22
  if (!configuration.includes('Debug')) {
18
23
  return;
19
24
  }
20
25
 
26
+ // nothing to copy across when only one slice was built
27
+ if (!sdks.includes('iphoneos') || !sdks.includes('iphonesimulator')) {
28
+ return;
29
+ }
30
+
21
31
  const deviceBundlePath = path.join(
22
32
  productsPath,
23
33
  `${configuration}-iphoneos`,