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# Most projects have their project root, one level up from their Xcode project dir (the "ios" directory)
62PROJECT_ROOT=${PROJECT_ROOT:-"$PROJECT_DIR/.."}
63
64cd "$PROJECT_ROOT" || exit
65
66# Define entry file
67if [[ "$ENTRY_FILE" ]]; then
68  # Use ENTRY_FILE defined by user
69  :
70elif [[ -s "index.ios.js" ]]; then
71  ENTRY_FILE=${1:-index.ios.js}
72else
73  ENTRY_FILE=${1:-index.js}
74fi
75
76# check and assign NODE_BINARY env
77# shellcheck source=/dev/null
78source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
79
80# If hermes-engine is in the Podfile.lock, it means that Hermes is a dependency of the project
81# and it is enabled. If not, it means that hermes is disabled.
82HERMES_ENABLED=$(grep hermes-engine $PODS_PODFILE_DIR_PATH/Podfile.lock)
83
84# If hermes-engine is not in the Podfile.lock, it means that the app is not using Hermes.
85# Setting USE_HERMES is no the only way to set whether the app can use hermes or not: users
86# can also modify manually the Podfile.
87if [[ -z "$HERMES_ENABLED" ]]; then
88  USE_HERMES=false
89fi
90
91HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine"
92[ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc"
93
94# Hermes is enabled in new projects by default, so we cannot assume that USE_HERMES=1 is set as an envvar.
95# If hermes-engine is found in Pods, we can assume Hermes has not been disabled.
96# If hermesc is not available and USE_HERMES is either unset or true, show error.
97if [[  ! -z "$HERMES_ENABLED" && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then
98  echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \
99       "Perhaps you need to run 'bundle exec pod install' or otherwise " \
100       "point the HERMES_CLI_PATH variable to your custom location." >&2
101  exit 2
102fi
103
104[ -z "$NODE_ARGS" ] && export NODE_ARGS=""
105
106[ -z "$CLI_PATH" ] && export CLI_PATH="$REACT_NATIVE_DIR/cli.js"
107
108[ -z "$BUNDLE_COMMAND" ] && BUNDLE_COMMAND="bundle"
109
110[ -z "$COMPOSE_SOURCEMAP_PATH" ] && COMPOSE_SOURCEMAP_PATH="$REACT_NATIVE_DIR/scripts/compose-source-maps.js"
111
112if [[ -z "$BUNDLE_CONFIG" ]]; then
113  CONFIG_ARG=""
114else
115  CONFIG_ARG="--config $BUNDLE_CONFIG"
116fi
117
118BUNDLE_FILE="$CONFIGURATION_BUILD_DIR/main.jsbundle"
119
120EXTRA_ARGS=
121
122case "$PLATFORM_NAME" in
123  "macosx")
124    BUNDLE_PLATFORM="macos"
125    ;;
126  *)
127    BUNDLE_PLATFORM="ios"
128    ;;
129esac
130
131if [ "${IS_MACCATALYST}" = "YES" ]; then
132  BUNDLE_PLATFORM="ios"
133fi
134
135EMIT_SOURCEMAP=
136if [[ ! -z "$SOURCEMAP_FILE" ]]; then
137  EMIT_SOURCEMAP=true
138fi
139
140PACKAGER_SOURCEMAP_FILE=
141if [[ $EMIT_SOURCEMAP == true ]]; then
142  if [[ $USE_HERMES != false ]]; then
143    PACKAGER_SOURCEMAP_FILE="$CONFIGURATION_BUILD_DIR/$(basename $SOURCEMAP_FILE)"
144  else
145    PACKAGER_SOURCEMAP_FILE="$SOURCEMAP_FILE"
146  fi
147  EXTRA_ARGS="$EXTRA_ARGS --sourcemap-output $PACKAGER_SOURCEMAP_FILE"
148fi
149
150# Hermes doesn't require JS minification.
151if [[ $USE_HERMES != false && $DEV == false ]]; then
152  EXTRA_ARGS="$EXTRA_ARGS --minify false"
153fi
154
155"$NODE_BINARY" $NODE_ARGS "$CLI_PATH" $BUNDLE_COMMAND \
156  $CONFIG_ARG \
157  --entry-file "$ENTRY_FILE" \
158  --platform "$BUNDLE_PLATFORM" \
159  --dev $DEV \
160  --reset-cache \
161  --bundle-output "$BUNDLE_FILE" \
162  --assets-dest "$DEST" \
163  $EXTRA_ARGS \
164  $EXTRA_PACKAGER_ARGS
165
166if [[ $USE_HERMES == false ]]; then
167  cp "$BUNDLE_FILE" "$DEST/"
168  BUNDLE_FILE="$DEST/main.jsbundle"
169else
170  EXTRA_COMPILER_ARGS=
171  if [[ $DEV == true ]]; then
172    EXTRA_COMPILER_ARGS=-Og
173  else
174    EXTRA_COMPILER_ARGS=-O
175  fi
176  if [[ $EMIT_SOURCEMAP == true ]]; then
177    EXTRA_COMPILER_ARGS="$EXTRA_COMPILER_ARGS -output-source-map"
178  fi
179  "$HERMES_CLI_PATH" -emit-binary -max-diagnostic-width=80 $EXTRA_COMPILER_ARGS -out "$DEST/main.jsbundle" "$BUNDLE_FILE"
180  if [[ $EMIT_SOURCEMAP == true ]]; then
181    HBC_SOURCEMAP_FILE="$DEST/main.jsbundle.map"
182    "$NODE_BINARY" "$COMPOSE_SOURCEMAP_PATH" "$PACKAGER_SOURCEMAP_FILE" "$HBC_SOURCEMAP_FILE" -o "$SOURCEMAP_FILE"
183    rm "$HBC_SOURCEMAP_FILE"
184    rm "$PACKAGER_SOURCEMAP_FILE"
185  fi
186  BUNDLE_FILE="$DEST/main.jsbundle"
187fi
188
189if [[ $DEV != true && ! -f "$BUNDLE_FILE" ]]; then
190  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
191  exit 2
192fi
193