1#!/bin/bash
2# Copyright (c) Meta Platforms, Inc. and affiliates.
3#
4# This source code is licensed under the MIT license found in the
5# LICENSE file in the root directory of this source tree.
6
7# Bundle React Native app's code and image assets.
8# This script is supposed to be invoked as part of Xcode build process
9# and relies on environment variables (including PWD) set by Xcode
10
11# Print commands before executing them (useful for troubleshooting)
12set -x
13DEST=$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH
14
15# Enables iOS devices to get the IP address of the machine running Metro
16if [[ ! "$SKIP_BUNDLING_METRO_IP" && "$CONFIGURATION" = *Debug* && ! "$PLATFORM_NAME" == *simulator ]]; then
17  for num in 0 1 2 3 4 5 6 7 8; do
18    IP=$(ipconfig getifaddr en${num})
19    if [ ! -z "$IP" ]; then
20      break
21    fi
22  done
23  if [ -z "$IP" ]; then
24    IP=$(ifconfig | grep 'inet ' | grep -v ' 127.' | grep -v ' 169.254.' |cut -d\   -f2  | awk 'NR==1{print $1}')
25  fi
26
27  echo "$IP" > "$DEST/ip.txt"
28fi
29
30if [[ "$SKIP_BUNDLING" ]]; then
31  echo "SKIP_BUNDLING enabled; skipping."
32  exit 0;
33fi
34
35case "$CONFIGURATION" in
36  *Debug*)
37    if [[ "$PLATFORM_NAME" == *simulator ]]; then
38      if [[ "$FORCE_BUNDLING" ]]; then
39        echo "FORCE_BUNDLING enabled; continuing to bundle."
40      else
41        echo "Skipping bundling in Debug for the Simulator (since the packager bundles for you). Use the FORCE_BUNDLING flag to change this behavior."
42        exit 0;
43      fi
44    else
45      echo "Bundling for physical device. Use the SKIP_BUNDLING flag to change this behavior."
46    fi
47
48    DEV=true
49    ;;
50  "")
51    echo "$0 must be invoked by Xcode"
52    exit 1
53    ;;
54  *)
55    DEV=false
56    ;;
57esac
58
59# Path to react-native folder inside node_modules
60REACT_NATIVE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
61# The project should be located next to where react-native is installed
62# in node_modules.
63PROJECT_ROOT=${PROJECT_ROOT:-"$REACT_NATIVE_DIR/../.."}
64
65cd "$PROJECT_ROOT" || exit
66
67# Define entry file
68if [[ "$ENTRY_FILE" ]]; then
69  # Use ENTRY_FILE defined by user
70  :
71elif [[ -s "index.ios.js" ]]; then
72  ENTRY_FILE=${1:-index.ios.js}
73else
74  ENTRY_FILE=${1:-index.js}
75fi
76
77# check and assign NODE_BINARY env
78# shellcheck source=/dev/null
79source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
80
81# If hermes-engine is in the Podfile.lock, it means that Hermes is a dependency of the project
82# and it is enabled. If not, it means that hermes is disabled.
83HERMES_ENABLED=$(grep hermes-engine $PODS_PODFILE_DIR_PATH/Podfile.lock)
84
85# If hermes-engine is not in the Podfile.lock, it means that the app is not using Hermes.
86# Setting USE_HERMES is no the only way to set whether the app can use hermes or not: users
87# can also modify manually the Podfile.
88if [[ -z "$HERMES_ENABLED" ]]; then
89  USE_HERMES=false
90fi
91
92HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine"
93[ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc"
94
95# Hermes is enabled in new projects by default, so we cannot assume that USE_HERMES=1 is set as an envvar.
96# If hermes-engine is found in Pods, we can assume Hermes has not been disabled.
97# If hermesc is not available and USE_HERMES is either unset or true, show error.
98if [[  ! -z "$HERMES_ENABLED" && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then
99  echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \
100       "Perhaps you need to run 'bundle exec pod install' or otherwise " \
101       "point the HERMES_CLI_PATH variable to your custom location." >&2
102  exit 2
103fi
104
105[ -z "$NODE_ARGS" ] && export NODE_ARGS=""
106
107[ -z "$CLI_PATH" ] && export CLI_PATH="$REACT_NATIVE_DIR/cli.js"
108
109[ -z "$BUNDLE_COMMAND" ] && BUNDLE_COMMAND="bundle"
110
111[ -z "$COMPOSE_SOURCEMAP_PATH" ] && COMPOSE_SOURCEMAP_PATH="$REACT_NATIVE_DIR/scripts/compose-source-maps.js"
112
113if [[ -z "$BUNDLE_CONFIG" ]]; then
114  CONFIG_ARG=""
115else
116  CONFIG_ARG="--config $BUNDLE_CONFIG"
117fi
118
119BUNDLE_FILE="$CONFIGURATION_BUILD_DIR/main.jsbundle"
120
121EXTRA_ARGS=
122
123case "$PLATFORM_NAME" in
124  "macosx")
125    BUNDLE_PLATFORM="macos"
126    ;;
127  *)
128    BUNDLE_PLATFORM="ios"
129    ;;
130esac
131
132if [ "${IS_MACCATALYST}" = "YES" ]; then
133  BUNDLE_PLATFORM="ios"
134fi
135
136EMIT_SOURCEMAP=
137if [[ ! -z "$SOURCEMAP_FILE" ]]; then
138  EMIT_SOURCEMAP=true
139fi
140
141PACKAGER_SOURCEMAP_FILE=
142if [[ $EMIT_SOURCEMAP == true ]]; then
143  if [[ $USE_HERMES != false ]]; then
144    PACKAGER_SOURCEMAP_FILE="$CONFIGURATION_BUILD_DIR/$(basename $SOURCEMAP_FILE)"
145  else
146    PACKAGER_SOURCEMAP_FILE="$SOURCEMAP_FILE"
147  fi
148  EXTRA_ARGS="$EXTRA_ARGS --sourcemap-output $PACKAGER_SOURCEMAP_FILE"
149fi
150
151# Hermes doesn't require JS minification.
152if [[ $USE_HERMES != false && $DEV == false ]]; then
153  EXTRA_ARGS="$EXTRA_ARGS --minify false"
154fi
155
156"$NODE_BINARY" $NODE_ARGS "$CLI_PATH" $BUNDLE_COMMAND \
157  $CONFIG_ARG \
158  --entry-file "$ENTRY_FILE" \
159  --platform "$BUNDLE_PLATFORM" \
160  --dev $DEV \
161  --reset-cache \
162  --bundle-output "$BUNDLE_FILE" \
163  --assets-dest "$DEST" \
164  $EXTRA_ARGS \
165  $EXTRA_PACKAGER_ARGS
166
167if [[ $USE_HERMES == false ]]; then
168  cp "$BUNDLE_FILE" "$DEST/"
169  BUNDLE_FILE="$DEST/main.jsbundle"
170else
171  EXTRA_COMPILER_ARGS=
172  if [[ $DEV == true ]]; then
173    EXTRA_COMPILER_ARGS=-Og
174  else
175    EXTRA_COMPILER_ARGS=-O
176  fi
177  if [[ $EMIT_SOURCEMAP == true ]]; then
178    EXTRA_COMPILER_ARGS="$EXTRA_COMPILER_ARGS -output-source-map"
179  fi
180  "$HERMES_CLI_PATH" -emit-binary $EXTRA_COMPILER_ARGS -out "$DEST/main.jsbundle" "$BUNDLE_FILE"
181  if [[ $EMIT_SOURCEMAP == true ]]; then
182    HBC_SOURCEMAP_FILE="$DEST/main.jsbundle.map"
183    "$NODE_BINARY" "$COMPOSE_SOURCEMAP_PATH" "$PACKAGER_SOURCEMAP_FILE" "$HBC_SOURCEMAP_FILE" -o "$SOURCEMAP_FILE"
184    rm "$HBC_SOURCEMAP_FILE"
185    rm "$PACKAGER_SOURCEMAP_FILE"
186  fi
187  BUNDLE_FILE="$DEST/main.jsbundle"
188fi
189
190if [[ $DEV != true && ! -f "$BUNDLE_FILE" ]]; then
191  echo "error: File $BUNDLE_FILE does not exist. This must be a bug with React Native, please report it here: https://github.com/facebook/react-native/issues" >&2
192  exit 2
193fi
194