expo-modules-jsi 56.0.1 → 56.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,13 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 56.0.2 — 2026-05-08
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Fixed `ExpoModulesJSI.xcframework` build failing under `useFrameworks: "static"` + `buildReactNativeFromSource: true` due to missing header search paths. ([#45508](https://github.com/expo/expo/pull/45508) by [@chrfalch](https://github.com/chrfalch))
18
+ - [iOS] Fixed missing slices in `ExpoModulesJSI.xcframework` causing `No such module 'ExpoModulesJSI'` build errors. ([#45542](https://github.com/expo/expo/pull/45542) by [@tsapeta](https://github.com/tsapeta))
19
+
13
20
  ## 56.0.1 — 2026-05-06
14
21
 
15
22
  _This version does not introduce any user-facing changes._
@@ -7,15 +7,16 @@ import Foundation
7
7
  let packageDir = URL(fileURLWithPath: #filePath).deletingLastPathComponent().path
8
8
  let podsRoot = resolvePodsRoot()
9
9
 
10
- // Header roots needed by ExpoModulesJSI and ExpoModulesJSI-Cxx. The same paths
11
- // exist in both prebuilt and source-built React Native layouts, so we don't
12
- // need to detect the mode.
10
+ // Header roots for ExpoModulesJSI and ExpoModulesJSI-Cxx. The
11
+ // Pods/Headers/Public paths cover no-frameworks and prebuilt-RN; the trailing
12
+ // entries fall back to canonical sources for the static + source-built RN
13
+ // combo, where each React-X / third-party-deps pod compiles as a static
14
+ // framework and its headers don't get mirrored to Pods/Headers/Public. Clang
15
+ // ignores missing `-I` paths, so they're no-ops elsewhere. `RN_ROOT` is
16
+ // forwarded from build-xcframework.sh (Node-resolved for hoisted monorepos).
13
17
  let publicHeaders = "\(podsRoot)/Headers/Public"
14
- // In source-built RN, third-party deps (folly, boost, etc.) live under
15
- // per-pod dirs like RCT-Folly/folly/. In prebuilt RN they're bundled into
16
- // ReactNativeDependencies. Some pods nest their headers (e.g. RCT-Folly/folly/),
17
- // others place them directly (e.g. boost/preprocessor/), so we include both
18
- // the Headers/Public root and the per-pod dirs for the nested ones.
18
+ let reactNative = ProcessInfo.processInfo.environment["RN_ROOT"]
19
+ ?? "\(podsRoot)/../../node_modules/react-native"
19
20
  let headerSearchPaths = [
20
21
  publicHeaders,
21
22
  "\(publicHeaders)/React-jsi",
@@ -33,6 +34,14 @@ let headerSearchPaths = [
33
34
  "\(publicHeaders)/DoubleConversion",
34
35
  "\(publicHeaders)/fmt",
35
36
  "\(publicHeaders)/fast_float",
37
+ "\(reactNative)/ReactCommon",
38
+ "\(reactNative)/ReactCommon/jsi",
39
+ "\(reactNative)/ReactCommon/runtimeexecutor",
40
+ "\(reactNative)/ReactCommon/callinvoker",
41
+ "\(podsRoot)/RCT-Folly",
42
+ "\(podsRoot)/fmt/include",
43
+ "\(podsRoot)/glog/src",
44
+ "\(podsRoot)/DoubleConversion",
36
45
  ]
37
46
 
38
47
  // Path to the generated module map for the `jsi` Clang module. The
@@ -6,8 +6,10 @@
6
6
  # but can also be invoked manually with PODS_ROOT set.
7
7
  #
8
8
  # Features:
9
- # - Hash-based caching: skips rebuild if source files haven't changed
10
- # - Additive slices: builds only the requested platform, preserves others
9
+ # - Per-slice hash-based caching: a slice is rebuilt only when its own
10
+ # recorded source hash differs from the current one. Other slices —
11
+ # including those built for other platforms in earlier runs — are left
12
+ # untouched.
11
13
  # - Reads React/JSI/Hermes headers directly from Pods/Headers/Public, so the
12
14
  # same configuration works for both prebuilt and source-built React Native
13
15
  # - Cleans .swiftinterface files for cross-compiler compatibility
@@ -20,13 +22,11 @@
20
22
  # PLATFORM_NAME (optional) Build for a specific platform (e.g. iphoneos, iphonesimulator).
21
23
  # When unset, builds for both iphoneos and iphonesimulator.
22
24
 
23
- set -eo pipefail
25
+ set -euo pipefail
24
26
 
25
27
  PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
26
28
  PACKAGE_NAME="ExpoModulesJSI"
27
29
  XCFRAMEWORK_PATH="${PACKAGE_DIR}/Products/${PACKAGE_NAME}.xcframework"
28
- SLICES_DIR="${PACKAGE_DIR}/.xcframework-slices"
29
- HASH_FILE="${SLICES_DIR}/.build-hash"
30
30
 
31
31
  CONFIGURATION="Release"
32
32
  DERIVED_DATA_PATH="${PACKAGE_DIR}/.DerivedData"
@@ -34,6 +34,8 @@ SPM_BUILD_PATH="${PACKAGE_DIR}/.build"
34
34
  SPM_WORKSPACE_PATH="${PACKAGE_DIR}/.swiftpm"
35
35
  BUILD_PRODUCTS_PATH="${DERIVED_DATA_PATH}/Build/Products"
36
36
 
37
+ source "${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
38
+
37
39
  CLEAN=false
38
40
 
39
41
  while [[ $# -gt 0 ]]; do
@@ -49,7 +51,6 @@ while [[ $# -gt 0 ]]; do
49
51
  esac
50
52
  done
51
53
 
52
- # Use colors only when stdout is a terminal.
53
54
  if [[ -t 1 ]]; then
54
55
  BLUE="\033[34m"
55
56
  RESET="\033[0m"
@@ -71,9 +72,10 @@ SOURCE_DIRS=(
71
72
  SOURCE_FILES=(
72
73
  "${PACKAGE_DIR}/Package.swift"
73
74
  "${PACKAGE_DIR}/scripts/build-xcframework.sh"
75
+ "${PACKAGE_DIR}/scripts/create-stub-xcframework.sh"
76
+ "${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
74
77
  )
75
78
 
76
- # Computes a SHA256 hash of all source files.
77
79
  compute_hash() {
78
80
  local all_files
79
81
  all_files=$(
@@ -111,11 +113,30 @@ platform_destination() {
111
113
  esac
112
114
  }
113
115
 
114
- # Builds a single framework slice for a given platform and stages it.
116
+ # Resolves the xcframework slice ID that a built platform should land in.
117
+ # Mirrors the slice IDs xcodebuild -create-xcframework would have assigned for
118
+ # a single-arch device build / dual-arch simulator build.
119
+ platform_slice_id() {
120
+ case "$1" in
121
+ iphoneos) echo "ios-arm64" ;;
122
+ iphonesimulator) echo "ios-arm64_x86_64-simulator" ;;
123
+ appletvos) echo "tvos-arm64" ;;
124
+ appletvsimulator) echo "tvos-arm64_x86_64-simulator" ;;
125
+ *)
126
+ log "error: No slice mapping for platform: $1"
127
+ exit 1
128
+ ;;
129
+ esac
130
+ }
131
+
132
+ # Builds a single framework slice for the given platform and replaces the
133
+ # matching slice inside XCFRAMEWORK_PATH. Other slices on disk are untouched.
115
134
  build_slice() {
116
135
  local platform="$1"
117
136
  local destination
118
137
  destination=$(platform_destination "$platform")
138
+ local slice_id
139
+ slice_id=$(platform_slice_id "$platform")
119
140
  local build_dir_name="${CONFIGURATION}-${platform}"
120
141
 
121
142
  log "Building framework slice for ${platform}..."
@@ -124,10 +145,10 @@ build_slice() {
124
145
 
125
146
  # Use env -i to clear inherited Xcode environment variables from the parent build.
126
147
  # Without this, the nested xcodebuild inherits SDKROOT, PLATFORM_NAME, etc.
127
- # which causes SDK/platform mismatches. PODS_ROOT is forwarded explicitly
128
- # because Package.swift reads it to resolve header search paths.
148
+ # which causes SDK/platform mismatches. PODS_ROOT and RN_ROOT are forwarded
149
+ # explicitly because Package.swift reads them to resolve header search paths.
129
150
  # Run from PACKAGE_DIR so xcodebuild finds the SPM package, not the Pods project.
130
- (cd "$PACKAGE_DIR" && env -i PATH="$PATH" HOME="$HOME" PODS_ROOT="$PODS_ROOT" \
151
+ (cd "$PACKAGE_DIR" && env -i PATH="$PATH" HOME="$HOME" PODS_ROOT="$PODS_ROOT" RN_ROOT="$RN_ROOT" \
131
152
  xcodebuild \
132
153
  build \
133
154
  -scheme "$PACKAGE_NAME" \
@@ -148,22 +169,29 @@ build_slice() {
148
169
  )
149
170
 
150
171
  local product_path="${BUILD_PRODUCTS_PATH}/${build_dir_name}"
151
- local framework_path="${product_path}/PackageFrameworks/${PACKAGE_NAME}.framework"
172
+ local framework_src="${product_path}/PackageFrameworks/${PACKAGE_NAME}.framework"
152
173
  local swiftmodule_src="${product_path}/${PACKAGE_NAME}.swiftmodule"
153
174
  local generated_maps="${DERIVED_DATA_PATH}/Build/Intermediates.noindex/GeneratedModuleMaps-${platform}"
154
175
 
155
- # Stage the built slice for this platform.
156
- local slice_staging="${SLICES_DIR}/${platform}"
157
- rm -rf "$slice_staging"
158
- mkdir -p "$slice_staging"
176
+ if [[ ! -d "$framework_src" ]]; then
177
+ log "error: xcodebuild did not produce ${framework_src}"
178
+ exit 1
179
+ fi
180
+
181
+ # Replace the slice in place. Stage to a temp location first so a partial
182
+ # write can't leave the xcframework in a broken state.
183
+ local slice_dir="${XCFRAMEWORK_PATH}/${slice_id}"
184
+ local staging_dir="${XCFRAMEWORK_PATH}/.${slice_id}.new"
185
+ rm -rf "$staging_dir"
186
+ mkdir -p "$staging_dir"
159
187
 
160
- cp -r "$framework_path" "$slice_staging/"
188
+ cp -r "$framework_src" "$staging_dir/"
161
189
  if [[ -d "${product_path}/${PACKAGE_NAME}.framework.dSYM" ]]; then
162
- cp -r "${product_path}/${PACKAGE_NAME}.framework.dSYM" "$slice_staging/"
190
+ cp -r "${product_path}/${PACKAGE_NAME}.framework.dSYM" "$staging_dir/"
163
191
  fi
164
192
 
165
193
  # Copy Swift module interfaces and generated headers into the staged framework.
166
- local modules_dir="${slice_staging}/${PACKAGE_NAME}.framework/Modules"
194
+ local modules_dir="${staging_dir}/${PACKAGE_NAME}.framework/Modules"
167
195
  mkdir -p "$modules_dir"
168
196
  cp -r "$swiftmodule_src/" "${modules_dir}/${PACKAGE_NAME}.swiftmodule"
169
197
  rm -rf "${modules_dir}/${PACKAGE_NAME}.swiftmodule/Project"
@@ -185,33 +213,15 @@ build_slice() {
185
213
  find "${modules_dir}/${PACKAGE_NAME}.swiftmodule" -name '*.swiftinterface' \
186
214
  -exec sed -i '' '/^extension __ObjC\./,/^}/d;/^@usableFromInline$/{N;/_ConstraintThatIsNotPartOfTheAPIOfThisLibrary/d;};/_ConstraintThatIsNotPartOfTheAPIOfThisLibrary/d' {} +
187
215
 
188
- local headers_dir="${slice_staging}/${PACKAGE_NAME}.framework/Headers"
216
+ local headers_dir="${staging_dir}/${PACKAGE_NAME}.framework/Headers"
189
217
  mkdir -p "$headers_dir"
190
218
  cp "${generated_maps}/${PACKAGE_NAME}-Swift.h" "$headers_dir/"
191
219
  cp "${generated_maps}/${PACKAGE_NAME}.modulemap" "$headers_dir/module.modulemap"
192
- }
193
220
 
194
- # Assembles the xcframework from all staged slices.
195
- assemble_xcframework() {
196
- rm -rf "$XCFRAMEWORK_PATH"
197
-
198
- local xcframework_args=()
199
- for slice_dir in "${SLICES_DIR}"/*/; do
200
- local framework="${slice_dir}${PACKAGE_NAME}.framework"
201
- local dsym="${slice_dir}${PACKAGE_NAME}.framework.dSYM"
202
- if [[ -d "$framework" ]]; then
203
- xcframework_args+=(-framework "$framework")
204
- if [[ -d "$dsym" ]]; then
205
- xcframework_args+=(-debug-symbols "$(cd "$dsym" && pwd)")
206
- fi
207
- fi
208
- done
209
-
210
- xcodebuild -create-xcframework "${xcframework_args[@]}" -output "$XCFRAMEWORK_PATH"
211
-
212
- # Write the hash so subsequent builds can skip if nothing changed.
213
- mkdir -p "$SLICES_DIR"
214
- echo "$current_hash" > "$HASH_FILE"
221
+ echo "$current_hash" > "${staging_dir}/.build-hash"
222
+
223
+ rm -rf "$slice_dir"
224
+ mv "$staging_dir" "$slice_dir"
215
225
  }
216
226
 
217
227
  # --- Main ---
@@ -233,6 +243,17 @@ fi
233
243
  # whether PODS_ROOT was passed as relative or absolute.
234
244
  PODS_ROOT="$(cd "$PODS_ROOT" && pwd)"
235
245
 
246
+ # Resolve react-native via Node so the build works in any node_modules layout
247
+ # (hoisted monorepos, yarn/pnpm workspaces). Falls back to the relative path
248
+ # when `node` is unavailable. Forwarded to Package.swift and the modulemap
249
+ # generator below.
250
+ RN_ROOT="$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null \
251
+ || echo "${PODS_ROOT}/../../node_modules/react-native")"
252
+
253
+ mode="$( [[ -d "${PODS_ROOT}/React-Core-prebuilt/React.xcframework" ]] && echo "prebuilt RN" || echo "source-built RN")"
254
+ [[ -f "${PODS_ROOT}/Target Support Files/React-jsi/React-jsi-umbrella.h" ]] && mode="${mode}, static frameworks"
255
+ log "Detected: ${mode} (RN_ROOT=${RN_ROOT})"
256
+
236
257
  # React Native version — forces a rebuild after an RN upgrade. The local
237
258
  # podspec is regenerated by `pod install` and only changes when the underlying
238
259
  # RN version changes.
@@ -241,17 +262,21 @@ if [[ -f "${PODS_ROOT}/Local Podspecs/React-Core.podspec.json" ]]; then
241
262
  fi
242
263
 
243
264
  # Generate the module map for the `jsi` Clang module.
244
- env PODS_ROOT="$PODS_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
265
+ env PODS_ROOT="$PODS_ROOT" RN_ROOT="$RN_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
245
266
  GENERATED_MODULE_MAP="${PACKAGE_DIR}/.generated/module.modulemap"
246
267
  SOURCE_FILES+=("$GENERATED_MODULE_MAP")
247
268
 
248
269
  if [[ "$CLEAN" == true ]]; then
249
- rm -rf "$XCFRAMEWORK_PATH" "$SLICES_DIR" "$DERIVED_DATA_PATH" "$SPM_BUILD_PATH" "$SPM_WORKSPACE_PATH"
250
- log "Cleaned existing xcframework, staged slices, DerivedData, and SwiftPM state"
270
+ rm -rf "$XCFRAMEWORK_PATH" "$DERIVED_DATA_PATH" "$SPM_BUILD_PATH" "$SPM_WORKSPACE_PATH"
271
+ log "Cleaned existing xcframework, DerivedData, and SwiftPM state"
272
+ # Re-stamp stub slices so the post-clean state matches a fresh `pod install`:
273
+ # CocoaPods reads Info.plist before this script runs, and would fail to
274
+ # resolve any slice not declared there.
275
+ "${PACKAGE_DIR}/scripts/create-stub-xcframework.sh"
251
276
  fi
252
277
 
253
278
  # Determine which platforms to build.
254
- if [[ -n "$PLATFORM_NAME" ]]; then
279
+ if [[ -n "${PLATFORM_NAME:-}" ]]; then
255
280
  PLATFORMS=("$PLATFORM_NAME")
256
281
  else
257
282
  PLATFORMS=("iphoneos" "iphonesimulator")
@@ -259,42 +284,31 @@ fi
259
284
 
260
285
  current_hash=$(compute_hash)
261
286
 
262
- # Check if sources have changed since the last build.
263
- if [[ -f "$HASH_FILE" ]]; then
264
- previous_hash=$(cat "$HASH_FILE")
265
- if [[ "$current_hash" == "$previous_hash" ]]; then
266
- # Sources haven't changed — filter out platforms that already have a staged slice.
267
- platforms_to_build=()
268
- for platform in "${PLATFORMS[@]}"; do
269
- if [[ ! -d "${SLICES_DIR}/${platform}/${PACKAGE_NAME}.framework" ]]; then
270
- platforms_to_build+=("$platform")
271
- fi
272
- done
273
-
274
- if [[ ${#platforms_to_build[@]} -eq 0 ]]; then
275
- log "xcframework is up to date, skipping build"
276
- exit 0
277
- fi
278
-
279
- PLATFORMS=("${platforms_to_build[@]}")
280
- else
281
- # Sources changed — remove all staged slices so they get rebuilt.
282
- # Also wipe DerivedData and .build because Swift Package Manager caches
283
- # the resolved package graph there and Package.swift reads PODS_ROOT at
284
- # resolve time; without this, switching PODS_ROOT (e.g. between apps)
285
- # leaks paths from the previous resolution into the new build.
286
- log "Source files changed, rebuilding all slices"
287
- rm -rf "$SLICES_DIR" "$XCFRAMEWORK_PATH" "$DERIVED_DATA_PATH" "$SPM_BUILD_PATH" "$SPM_WORKSPACE_PATH"
287
+ # Filter out platforms whose slice is already up to date.
288
+ platforms_to_build=()
289
+ for platform in "${PLATFORMS[@]}"; do
290
+ slice_id=$(platform_slice_id "$platform")
291
+ slice_hash_file="${XCFRAMEWORK_PATH}/${slice_id}/.build-hash"
292
+ if [[ -f "$slice_hash_file" ]] && [[ "$(cat "$slice_hash_file")" == "$current_hash" ]]; then
293
+ continue
288
294
  fi
295
+ platforms_to_build+=("$platform")
296
+ done
297
+
298
+ if [[ ${#platforms_to_build[@]} -eq 0 ]]; then
299
+ log "xcframework slices up to date, skipping build"
300
+ exit 0
289
301
  fi
290
302
 
303
+ PLATFORMS=("${platforms_to_build[@]}")
304
+
291
305
  SECONDS=0
292
306
 
293
307
  for platform in "${PLATFORMS[@]}"; do
294
308
  build_slice "$platform"
295
309
  done
296
310
 
297
- assemble_xcframework
311
+ write_xcframework_plist "$XCFRAMEWORK_PATH" "$PACKAGE_NAME"
298
312
 
299
- SLICE_NAMES=$(for d in "${XCFRAMEWORK_PATH}"/*/; do basename "$d"; done | paste -sd', ' - | sed 's/,/, /g')
313
+ SLICE_NAMES=$(for d in "${XCFRAMEWORK_PATH}"/*/; do basename "$d"; done | LC_ALL=C sort | tr '\n' ',' | sed 's/,$//;s/,/, /g')
300
314
  log "Built xcframework successfully in ${SECONDS}s (${SLICE_NAMES})"
@@ -1,102 +1,57 @@
1
1
  #!/bin/bash
2
2
 
3
- # Creates a stub xcframework with a minimal dynamic library so that
4
- # CocoaPods detects it as a dynamic framework and generates the
5
- # "[CP] Copy XCFrameworks" and "[CP] Embed Pods Frameworks" build phases.
6
- # The real xcframework is built by the build-xcframework.sh script at build time.
3
+ # Ensures every required slice in ExpoModulesJSI.xcframework exists with at
4
+ # least a stub binary so CocoaPods detects the package as a dynamic framework
5
+ # and generates the "[CP] Copy XCFrameworks" and "[CP] Embed Pods Frameworks"
6
+ # build phases for every supported platform. The real xcframework is built by
7
+ # build-xcframework.sh at build time.
8
+ #
9
+ # Non-destructive: existing slice binaries (real or stub) are kept; only
10
+ # missing required slices are stamped. Info.plist is rewritten from whatever
11
+ # slices are on disk so additional slices built via Xcode survive.
7
12
 
8
- set -eo pipefail
13
+ set -euo pipefail
9
14
 
10
15
  PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
11
16
  PACKAGE_NAME="ExpoModulesJSI"
12
17
  XCFRAMEWORK_PATH="${PACKAGE_DIR}/Products/${PACKAGE_NAME}.xcframework"
13
18
 
14
- # Use colors only when run in the terminal
19
+ source "$(dirname "${BASH_SOURCE[0]}")/xcframework-helpers.sh"
20
+
15
21
  BLUE=""; RESET=""
16
22
  if [ -t 1 ]; then
17
23
  BLUE="\033[34m"
18
24
  RESET="\033[0m"
19
25
  fi
20
- echo -e "${BLUE}[Expo]${RESET} Creating stub xcframework for ${PACKAGE_NAME}"
21
-
22
- # Platform slices the podspec supports. CocoaPods reads Info.plist at
23
- # `pod install` time to generate per-slice cases in its xcframework copy
24
- # script, so every SDK we want to build for must appear here.
25
- #
26
- # Format: slice_id|platform|variant|archs
27
- SLICES=(
28
- "ios-arm64|ios||arm64"
29
- "ios-arm64_x86_64-simulator|ios|simulator|arm64 x86_64"
30
- "tvos-arm64|tvos||arm64"
31
- "tvos-arm64_x86_64-simulator|tvos|simulator|arm64 x86_64"
32
- )
33
-
34
- # Always regenerate the Info.plist so it declares every slice. A previous
35
- # build may have produced an xcframework with only the target platform's
36
- # slice (e.g. device-only or simulator-only), which would cause CocoaPods
37
- # to generate a copy script that is missing the other variant.
26
+ echo -e "${BLUE}[Expo]${RESET} Ensuring required slices in ${PACKAGE_NAME}.xcframework"
38
27
 
39
28
  # The stub binary is only inspected by CocoaPods at install time to detect
40
29
  # that this is a dynamic framework — it is never linked against (the real
41
30
  # xcframework replaces it before the sources compile). Compile it once and
42
- # reuse the same binary for every slice.
43
- STUB_SOURCE="${XCFRAMEWORK_PATH}/.stub-binary"
31
+ # reuse the same binary for every slice that needs stamping.
44
32
  mkdir -p "$XCFRAMEWORK_PATH"
33
+ STUB_SOURCE="${XCFRAMEWORK_PATH}/.stub-binary"
45
34
  echo "" | clang -x c - -dynamiclib \
46
35
  -o "$STUB_SOURCE" \
47
36
  -install_name "@rpath/${PACKAGE_NAME}.framework/${PACKAGE_NAME}"
48
37
 
49
- available_libraries=""
50
- for slice in "${SLICES[@]}"; do
51
- IFS='|' read -r slice_id platform variant archs <<<"$slice"
52
- slice_dir="${XCFRAMEWORK_PATH}/${slice_id}/${PACKAGE_NAME}.framework"
53
- mkdir -p "$slice_dir"
54
- if [[ ! -f "${slice_dir}/${PACKAGE_NAME}" ]]; then
55
- cp "$STUB_SOURCE" "${slice_dir}/${PACKAGE_NAME}"
56
- fi
57
-
58
- arch_entries=""
59
- for arch in $archs; do
60
- arch_entries+=" <string>${arch}</string>
61
- "
62
- done
38
+ for slice_id in "${EXPO_MODULES_JSI_REQUIRED_SLICE_IDS[@]}"; do
39
+ slice_dir="${XCFRAMEWORK_PATH}/${slice_id}"
40
+ framework_dir="${slice_dir}/${PACKAGE_NAME}.framework"
41
+ binary_path="${framework_dir}/${PACKAGE_NAME}"
42
+ hash_file="${slice_dir}/.build-hash"
63
43
 
64
- variant_entry=""
65
- if [[ -n "$variant" ]]; then
66
- variant_entry=" <key>SupportedPlatformVariant</key>
67
- <string>${variant}</string>
68
- "
44
+ mkdir -p "$framework_dir"
45
+ if [[ ! -f "$binary_path" ]]; then
46
+ cp "$STUB_SOURCE" "$binary_path"
47
+ fi
48
+ # Empty hash marks the slice as a stub. build-xcframework.sh treats any
49
+ # mismatch (including stub vs. real source hash) as needing a rebuild.
50
+ if [[ ! -f "$hash_file" ]]; then
51
+ : > "$hash_file"
69
52
  fi
70
-
71
- available_libraries+=" <dict>
72
- <key>BinaryPath</key>
73
- <string>${PACKAGE_NAME}.framework/${PACKAGE_NAME}</string>
74
- <key>LibraryIdentifier</key>
75
- <string>${slice_id}</string>
76
- <key>LibraryPath</key>
77
- <string>${PACKAGE_NAME}.framework</string>
78
- <key>SupportedArchitectures</key>
79
- <array>
80
- ${arch_entries} </array>
81
- <key>SupportedPlatform</key>
82
- <string>${platform}</string>
83
- ${variant_entry} </dict>
84
- "
85
53
  done
54
+
86
55
  rm -f "$STUB_SOURCE"
87
56
 
88
- cat > "${XCFRAMEWORK_PATH}/Info.plist" <<PLIST
89
- <?xml version="1.0" encoding="UTF-8"?>
90
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
91
- <plist version="1.0">
92
- <dict>
93
- <key>AvailableLibraries</key>
94
- <array>
95
- ${available_libraries} </array>
96
- <key>CFBundlePackageType</key>
97
- <string>XFWK</string>
98
- <key>XCFrameworkFormatVersion</key>
99
- <string>1.0</string>
100
- </dict>
101
- </plist>
102
- PLIST
57
+ write_xcframework_plist "$XCFRAMEWORK_PATH" "$PACKAGE_NAME"
@@ -3,11 +3,13 @@
3
3
  # Writes `.generated/module.modulemap` for the `jsi` Clang module.
4
4
  #
5
5
  # The umbrella header is referenced by absolute path so the modulemap works
6
- # regardless of where Pods lives. The same Pods/Headers/Public/React-jsi/jsi/jsi.h
7
- # path exists in both prebuilt and source-built React Native layouts. Stored
8
- # outside `.build/` so SwiftPM state can be wiped without losing this file.
6
+ # regardless of where Pods lives. The `Pods/Headers/Public` path exists in
7
+ # non-frameworks and prebuilt-RN layouts; under static frameworks +
8
+ # source-built RN it doesn't, so we fall back to the canonical RN source.
9
+ # Stored outside `.build/` so SwiftPM state can be wiped without losing this
10
+ # file.
9
11
  #
10
- # Idempotent: re-running with the same PODS_ROOT rewrites identical content.
12
+ # Idempotent: re-running with the same inputs rewrites identical content.
11
13
  # Switching PODS_ROOT updates the umbrella header path.
12
14
  #
13
15
  # Used by build-xcframework.sh and test.sh; can also be run manually before
@@ -34,10 +36,17 @@ GENERATED_DIR="${PACKAGE_DIR}/.generated"
34
36
  GENERATED_MODULE_MAP="${GENERATED_DIR}/module.modulemap"
35
37
  mkdir -p "$GENERATED_DIR"
36
38
 
39
+ JSI_UMBRELLA="${PODS_ROOT}/Headers/Public/React-jsi/jsi/jsi.h"
40
+ if [[ ! -f "$JSI_UMBRELLA" ]]; then
41
+ RN="${RN_ROOT:-$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null || echo "${PODS_ROOT}/../../node_modules/react-native")}"
42
+ JSI_UMBRELLA="${RN}/ReactCommon/jsi/jsi/jsi.h"
43
+ fi
44
+ [[ -f "$JSI_UMBRELLA" ]] || { echo "error: cannot locate jsi.h" >&2; exit 1; }
45
+
37
46
  # Avoid touching the file when contents would be identical, so the xcframework
38
- # hash cache and Xcode don't see a spurious change when PODS_ROOT is unchanged.
47
+ # hash cache and Xcode don't see a spurious change when inputs are unchanged.
39
48
  NEW_CONTENT="module jsi {
40
- umbrella header \"${PODS_ROOT}/Headers/Public/React-jsi/jsi/jsi.h\"
49
+ umbrella header \"${JSI_UMBRELLA}\"
41
50
 
42
51
  export *
43
52
  module * { export * }
@@ -0,0 +1,124 @@
1
+ # Shared helpers for managing ExpoModulesJSI.xcframework.
2
+ #
3
+ # Sourced by create-stub-xcframework.sh and build-xcframework.sh. Defines the
4
+ # canonical slice metadata and the Info.plist writer used by both scripts so
5
+ # the manifest is produced from a single place.
6
+
7
+ # All slice IDs known to the xcframework, mapped to their plist metadata.
8
+ # CocoaPods reads Info.plist at `pod install` time and generates a per-slice
9
+ # copy script; any slice missing from this table will be skipped by
10
+ # write_xcframework_plist with a warning.
11
+ #
12
+ # Format: slice_id|platform|variant|archs
13
+ EXPO_MODULES_JSI_KNOWN_SLICES=(
14
+ "ios-arm64|ios||arm64"
15
+ "ios-arm64_x86_64-simulator|ios|simulator|arm64 x86_64"
16
+ "tvos-arm64|tvos||arm64"
17
+ "tvos-arm64_x86_64-simulator|tvos|simulator|arm64 x86_64"
18
+ )
19
+
20
+ # Slice IDs the xcframework must always declare, even when only a subset has
21
+ # been built. The stub script materializes empty placeholders for any of these
22
+ # missing on disk so CocoaPods can wire up copy/embed phases for every target
23
+ # the podspec supports.
24
+ EXPO_MODULES_JSI_REQUIRED_SLICE_IDS=(
25
+ "ios-arm64"
26
+ "ios-arm64_x86_64-simulator"
27
+ "tvos-arm64"
28
+ "tvos-arm64_x86_64-simulator"
29
+ )
30
+
31
+ # xcframework_slice_descriptor SLICE_ID
32
+ # Echoes the descriptor row for SLICE_ID, or returns 1 if SLICE_ID is unknown.
33
+ xcframework_slice_descriptor() {
34
+ local slice_id="$1"
35
+ local entry
36
+ for entry in "${EXPO_MODULES_JSI_KNOWN_SLICES[@]}"; do
37
+ if [[ "${entry%%|*}" == "$slice_id" ]]; then
38
+ echo "$entry"
39
+ return 0
40
+ fi
41
+ done
42
+ return 1
43
+ }
44
+
45
+ # write_xcframework_plist XCFRAMEWORK_PATH PACKAGE_NAME
46
+ # Writes Info.plist describing every slice directory currently inside
47
+ # XCFRAMEWORK_PATH. Slices are sorted by ID for deterministic output. Slice
48
+ # directories not listed in EXPO_MODULES_JSI_KNOWN_SLICES are skipped with a
49
+ # warning so callers can spot missing metadata instead of silently shipping a
50
+ # slice CocoaPods can't describe.
51
+ write_xcframework_plist() {
52
+ local xcframework_path="$1"
53
+ local package_name="$2"
54
+
55
+ local slice_dirs=()
56
+ local entry
57
+ for entry in "${xcframework_path}"/*/; do
58
+ [[ -d "$entry" ]] || continue
59
+ slice_dirs+=("$(basename "$entry")")
60
+ done
61
+
62
+ local sorted_slices=()
63
+ while IFS= read -r line; do
64
+ sorted_slices+=("$line")
65
+ done < <(printf '%s\n' "${slice_dirs[@]}" | LC_ALL=C sort)
66
+
67
+ local available_libraries=""
68
+ local slice_id
69
+ for slice_id in "${sorted_slices[@]}"; do
70
+ local descriptor
71
+ if ! descriptor=$(xcframework_slice_descriptor "$slice_id"); then
72
+ echo "warning: skipping slice '${slice_id}' — not in EXPO_MODULES_JSI_KNOWN_SLICES" >&2
73
+ continue
74
+ fi
75
+
76
+ local platform variant archs
77
+ IFS='|' read -r _ platform variant archs <<<"$descriptor"
78
+
79
+ local arch_entries=""
80
+ local arch
81
+ for arch in $archs; do
82
+ arch_entries+=" <string>${arch}</string>
83
+ "
84
+ done
85
+
86
+ local variant_entry=""
87
+ if [[ -n "$variant" ]]; then
88
+ variant_entry=" <key>SupportedPlatformVariant</key>
89
+ <string>${variant}</string>
90
+ "
91
+ fi
92
+
93
+ available_libraries+=" <dict>
94
+ <key>BinaryPath</key>
95
+ <string>${package_name}.framework/${package_name}</string>
96
+ <key>LibraryIdentifier</key>
97
+ <string>${slice_id}</string>
98
+ <key>LibraryPath</key>
99
+ <string>${package_name}.framework</string>
100
+ <key>SupportedArchitectures</key>
101
+ <array>
102
+ ${arch_entries} </array>
103
+ <key>SupportedPlatform</key>
104
+ <string>${platform}</string>
105
+ ${variant_entry} </dict>
106
+ "
107
+ done
108
+
109
+ cat > "${xcframework_path}/Info.plist" <<PLIST
110
+ <?xml version="1.0" encoding="UTF-8"?>
111
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
112
+ <plist version="1.0">
113
+ <dict>
114
+ <key>AvailableLibraries</key>
115
+ <array>
116
+ ${available_libraries} </array>
117
+ <key>CFBundlePackageType</key>
118
+ <string>XFWK</string>
119
+ <key>XCFrameworkFormatVersion</key>
120
+ <string>1.0</string>
121
+ </dict>
122
+ </plist>
123
+ PLIST
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-jsi",
3
- "version": "56.0.1",
3
+ "version": "56.0.2",
4
4
  "description": "The JavaScript Interface for Expo Modules",
5
5
  "main": "index.js",
6
6
  "sideEffects": [],
@@ -41,7 +41,7 @@
41
41
  "./apple/scripts/test.sh"
42
42
  ]
43
43
  },
44
- "gitHead": "a8ab3da510a34b7bdb2262aa9887d4f78b102280",
44
+ "gitHead": "a30353e69ca0d72b9fac5830abc631feda1ba3ae",
45
45
  "scripts": {
46
46
  "build": "apple/scripts/build-xcframework.sh",
47
47
  "test": "apple/scripts/test.sh"