mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
99
Commits
@@ -35,6 +35,9 @@ module.exports = {
|
||||
// Flow handles these checks for us, so they aren't required
|
||||
'no-undef': 'off',
|
||||
'no-unreachable': 'off',
|
||||
// Throwing from function or rejecting promises with non-error values could result in unclear error stack traces and lead to harder debugging
|
||||
'prefer-promise-reject-errors': 'error',
|
||||
'no-throw-literal': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -98,4 +98,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.288.0
|
||||
^0.289.0
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
name: build-apple-slices-hermes
|
||||
description: This action builds hermesc for Apple platforms
|
||||
inputs:
|
||||
hermes-version:
|
||||
required: true
|
||||
description: The version of Hermes
|
||||
react-native-version:
|
||||
required: true
|
||||
description: The version of Hermes
|
||||
slice:
|
||||
required: true
|
||||
description: The slice of hermes you want to build. It could be iphone, iphonesimulator, macos, catalyst, appletvos, appletvsimulator, xros, or xrossimulator
|
||||
flavor:
|
||||
required: true
|
||||
description: The flavor we want to build. It can be Debug or Release
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- name: Restore Hermes workspace
|
||||
uses: ./.github/actions/restore-hermes-workspace
|
||||
- name: Restore HermesC Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermesc-apple
|
||||
path: ./packages/react-native/sdks/hermes/build_host_hermesc
|
||||
- name: Restore Slice From Cache
|
||||
id: restore-slice-cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/build_${{ inputs.slice }}_${{ inputs.flavor }}
|
||||
key: v6-hermes-apple-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}-${{ hashfiles('packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh') }}-${{ inputs.slice }}-${{ inputs.flavor }}
|
||||
- name: Build the Hermes ${{ inputs.slice }} frameworks
|
||||
shell: bash
|
||||
run: |
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
SLICE=${{ inputs.slice }}
|
||||
FLAVOR=${{ inputs.flavor }}
|
||||
FINAL_PATH=build_"$SLICE"_"$FLAVOR"
|
||||
echo "Final path for this slice is: $FINAL_PATH"
|
||||
|
||||
if [[ -d "$FINAL_PATH" ]]; then
|
||||
echo "[HERMES] Skipping! Found the requested slice at $FINAL_PATH".
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$ARTIFACTS_EXIST" ]]; then
|
||||
echo "[HERMES] Skipping! Artifacts exists already."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export RELEASE_VERSION=${{ inputs.react-native-version }}
|
||||
|
||||
# HermesC is used to build hermes, so it has to be executable
|
||||
chmod +x ./build_host_hermesc/bin/hermesc
|
||||
|
||||
if [[ "$SLICE" == "macosx" ]]; then
|
||||
echo "[HERMES] Building Hermes for MacOS"
|
||||
|
||||
chmod +x ./utils/build-mac-framework.sh
|
||||
BUILD_TYPE="${{ inputs.flavor }}" ./utils/build-mac-framework.sh
|
||||
else
|
||||
echo "[HERMES] Building Hermes for iOS: $SLICE"
|
||||
|
||||
chmod +x ./utils/build-ios-framework.sh
|
||||
BUILD_TYPE="${{ inputs.flavor }}" ./utils/build-ios-framework.sh "$SLICE"
|
||||
fi
|
||||
|
||||
echo "Moving from build_$SLICE to $FINAL_PATH"
|
||||
mv build_"$SLICE" "$FINAL_PATH"
|
||||
|
||||
# check whether everything is there
|
||||
if [[ -d "$FINAL_PATH/lib/hermesvm.framework" ]]; then
|
||||
echo "Successfully built hermesvm.framework for $SLICE in $FLAVOR"
|
||||
else
|
||||
echo "Failed to built hermesvm.framework for $SLICE in $FLAVOR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "$FINAL_PATH/lib/hermesvm.framework.dSYM" ]]; then
|
||||
echo "Successfully built hermesvm.framework.dSYM for $SLICE in $FLAVOR"
|
||||
else
|
||||
echo "Failed to built hermesvm.framework.dSYM for $SLICE in $FLAVOR"
|
||||
echo "Please try again"
|
||||
exit 1
|
||||
fi
|
||||
- name: Compress slices to preserve Symlinks
|
||||
shell: bash
|
||||
run: |
|
||||
cd ./packages/react-native/sdks/hermes
|
||||
tar -czv -f build_${{ matrix.slice }}_${{ matrix.flavor }}.tar.gz build_${{ matrix.slice }}_${{ matrix.flavor }}
|
||||
- name: Upload Artifact for Slice (${{ inputs.slice }}, ${{ inputs.flavor }}}
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: slice-${{ inputs.slice }}-${{ inputs.flavor }}
|
||||
path: ./packages/react-native/sdks/hermes/build_${{ inputs.slice }}_${{ inputs.flavor }}.tar.gz
|
||||
- name: Save slice cache
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, '-stable') }} # To avoid that the cache explode.
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/build_${{ inputs.slice }}_${{ inputs.flavor }}
|
||||
key: v6-hermes-apple-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}-${{ hashfiles('packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh') }}-${{ inputs.SLICE }}-${{ inputs.FLAVOR }}
|
||||
@@ -1,227 +0,0 @@
|
||||
name: build-hermes-macos
|
||||
description: This action builds hermesc for Apple platforms
|
||||
inputs:
|
||||
hermes-version:
|
||||
required: true
|
||||
description: The version of Hermes
|
||||
react-native-version:
|
||||
required: true
|
||||
description: The version of React Native
|
||||
flavor:
|
||||
required: true
|
||||
description: The flavor we want to build. It can be Debug or Release
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- name: Restore Hermes workspace
|
||||
uses: ./.github/actions/restore-hermes-workspace
|
||||
- name: Restore Cached Artifacts
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: v4-hermes-artifacts-${{ inputs.flavor }}-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}-${{ hashFiles('./packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh') }}
|
||||
path: |
|
||||
/tmp/hermes/osx-bin/${{ inputs.flavor }}
|
||||
/tmp/hermes/dSYM/${{ inputs.flavor }}
|
||||
/tmp/hermes/hermes-runtime-darwin/hermes-ios-${{ inputs.flavor }}.tar.gz
|
||||
- name: Check if the required artifacts already exist
|
||||
id: check_if_apple_artifacts_are_there
|
||||
shell: bash
|
||||
run: |
|
||||
FLAVOR="${{ inputs.flavor }}"
|
||||
echo "Flavor is $FLAVOR"
|
||||
OSX_BIN="/tmp/hermes/osx-bin/$FLAVOR"
|
||||
DSYM="/tmp/hermes/dSYM/$FLAVOR"
|
||||
HERMES="/tmp/hermes/hermes-runtime-darwin/hermes-ios-$FLAVOR.tar.gz"
|
||||
|
||||
if [[ -d "$OSX_BIN" ]] && \
|
||||
[[ -d "$DSYM" ]] && \
|
||||
[[ -f "$HERMES" ]]; then
|
||||
|
||||
echo "Artifacts are there!"
|
||||
echo "ARTIFACTS_EXIST=true" >> $GITHUB_ENV
|
||||
echo "ARTIFACTS_EXIST=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Yarn- Install Dependencies
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Slice cache macosx
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-macosx-${{ inputs.flavor }}
|
||||
- name: Slice cache iphoneos
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-iphoneos-${{ inputs.flavor }}
|
||||
- name: Slice cache iphonesimulator
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-iphonesimulator-${{ inputs.flavor }}
|
||||
- name: Slice cache appletvos
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-appletvos-${{ inputs.flavor }}
|
||||
- name: Slice cache appletvsimulator
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-appletvsimulator-${{ inputs.flavor }}
|
||||
- name: Slice cache catalyst
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-catalyst-${{ inputs.flavor }}
|
||||
- name: Slice cache xros
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-xros-${{ inputs.flavor }}
|
||||
- name: Slice cache xrsimulator
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/
|
||||
name: slice-xrsimulator-${{ inputs.flavor }}
|
||||
- name: Unzip slices
|
||||
shell: bash
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
run: |
|
||||
cd ./packages/react-native/sdks/hermes
|
||||
ls -l .
|
||||
tar -xzv -f build_catalyst_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_iphoneos_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_iphonesimulator_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_appletvos_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_appletvsimulator_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_macosx_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_xros_${{ matrix.flavor }}.tar.gz
|
||||
tar -xzv -f build_xrsimulator_${{ matrix.flavor }}.tar.gz
|
||||
- name: Move back build folders
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
ls -l ./packages/react-native/sdks/hermes
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
mv build_macosx_${{ inputs.flavor }} build_macosx
|
||||
mv build_iphoneos_${{ inputs.flavor }} build_iphoneos
|
||||
mv build_iphonesimulator_${{ inputs.flavor }} build_iphonesimulator
|
||||
mv build_appletvos_${{ inputs.flavor }} build_appletvos
|
||||
mv build_appletvsimulator_${{ inputs.flavor }} build_appletvsimulator
|
||||
mv build_catalyst_${{ inputs.flavor }} build_catalyst
|
||||
mv build_xros_${{ inputs.flavor }} build_xros
|
||||
mv build_xrsimulator_${{ inputs.flavor }} build_xrsimulator
|
||||
- name: Prepare destroot folder
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
chmod +x ./utils/build-apple-framework.sh
|
||||
. ./utils/build-apple-framework.sh
|
||||
prepare_dest_root_for_ci
|
||||
- name: Create fat framework for iOS
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
echo "[HERMES] Creating the universal framework"
|
||||
chmod +x ./utils/build-ios-framework.sh
|
||||
./utils/build-ios-framework.sh build_framework
|
||||
|
||||
chmod +x ./destroot/bin/hermesc
|
||||
- name: Package the Hermes Apple frameworks
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
BUILD_TYPE="${{ inputs.flavor }}"
|
||||
echo "Packaging Hermes Apple frameworks for $BUILD_TYPE build type"
|
||||
|
||||
TARBALL_OUTPUT_DIR=$(mktemp -d /tmp/hermes-tarball-output-XXXXXXXX)
|
||||
|
||||
TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE")
|
||||
|
||||
echo "Packaging Hermes Apple frameworks for $BUILD_TYPE build type"
|
||||
|
||||
TARBALL_OUTPUT_PATH=$(node ./packages/react-native/scripts/hermes/create-tarball.js \
|
||||
--inputDir ./packages/react-native/sdks/hermes \
|
||||
--buildType "$BUILD_TYPE" \
|
||||
--outputDir $TARBALL_OUTPUT_DIR)
|
||||
|
||||
echo "Hermes tarball saved to $TARBALL_OUTPUT_PATH"
|
||||
|
||||
mkdir -p $HERMES_TARBALL_ARTIFACTS_DIR
|
||||
cp $TARBALL_OUTPUT_PATH $HERMES_TARBALL_ARTIFACTS_DIR/.
|
||||
|
||||
mkdir -p /tmp/hermes/osx-bin/${{ inputs.flavor }}
|
||||
cp ./packages/react-native/sdks/hermes/build_macosx/bin/* /tmp/hermes/osx-bin/${{ inputs.flavor }}
|
||||
ls -lR /tmp/hermes/osx-bin/
|
||||
- name: Create dSYM archive
|
||||
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
FLAVOR=${{ inputs.flavor }}
|
||||
WORKING_DIR="/tmp/hermes_tmp/dSYM/$FLAVOR"
|
||||
|
||||
mkdir -p "$WORKING_DIR/macosx"
|
||||
mkdir -p "$WORKING_DIR/catalyst"
|
||||
mkdir -p "$WORKING_DIR/iphoneos"
|
||||
mkdir -p "$WORKING_DIR/iphonesimulator"
|
||||
mkdir -p "$WORKING_DIR/appletvos"
|
||||
mkdir -p "$WORKING_DIR/appletvsimulator"
|
||||
mkdir -p "$WORKING_DIR/xros"
|
||||
mkdir -p "$WORKING_DIR/xrsimulator"
|
||||
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
|
||||
DSYM_FILE_PATH=lib/hermesvm.framework.dSYM
|
||||
cp -r build_macosx/$DSYM_FILE_PATH "$WORKING_DIR/macosx/"
|
||||
cp -r build_catalyst/$DSYM_FILE_PATH "$WORKING_DIR/catalyst/"
|
||||
cp -r build_iphoneos/$DSYM_FILE_PATH "$WORKING_DIR/iphoneos/"
|
||||
cp -r build_iphonesimulator/$DSYM_FILE_PATH "$WORKING_DIR/iphonesimulator/"
|
||||
cp -r build_appletvos/$DSYM_FILE_PATH "$WORKING_DIR/appletvos/"
|
||||
cp -r build_appletvsimulator/$DSYM_FILE_PATH "$WORKING_DIR/appletvsimulator/"
|
||||
cp -r build_xros/$DSYM_FILE_PATH "$WORKING_DIR/xros/"
|
||||
cp -r build_xrsimulator/$DSYM_FILE_PATH "$WORKING_DIR/xrsimulator/"
|
||||
|
||||
DEST_DIR="/tmp/hermes/dSYM/$FLAVOR"
|
||||
tar -C "$WORKING_DIR" -czvf "hermesvm.framework.dSYM" .
|
||||
|
||||
mkdir -p "$DEST_DIR"
|
||||
mv "hermesvm.framework.dSYM" "$DEST_DIR"
|
||||
- name: Upload hermes dSYM artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermes-dSYM-${{ inputs.flavor }}
|
||||
path: /tmp/hermes/dSYM/${{ inputs.flavor }}
|
||||
- name: Upload hermes Runtime artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermes-darwin-bin-${{ inputs.flavor }}
|
||||
path: /tmp/hermes/hermes-runtime-darwin/hermes-ios-${{ inputs.flavor }}.tar.gz
|
||||
- name: Upload hermes osx artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermes-osx-bin-${{ inputs.flavor }}
|
||||
path: /tmp/hermes/osx-bin/${{ inputs.flavor }}
|
||||
- name: Upload Hermes Artifacts
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, '-stable') }} # To avoid that the cache explode.
|
||||
with:
|
||||
key: v4-hermes-artifacts-${{ inputs.flavor }}-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}-${{ hashFiles('./packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh') }}
|
||||
path: |
|
||||
/tmp/hermes/osx-bin/${{ inputs.flavor }}
|
||||
/tmp/hermes/dSYM/${{ inputs.flavor }}
|
||||
/tmp/hermes/hermes-runtime-darwin/hermes-ios-${{ inputs.flavor }}.tar.gz
|
||||
@@ -1,39 +0,0 @@
|
||||
name: build-hermesc-apple
|
||||
description: This action builds hermesc for Apple platforms
|
||||
inputs:
|
||||
hermes-version:
|
||||
required: true
|
||||
description: The version of Hermes
|
||||
react-native-version:
|
||||
required: true
|
||||
description: The version of React Native
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- name: Restore Hermes workspace
|
||||
uses: ./.github/actions/restore-hermes-workspace
|
||||
- name: Hermes apple cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/build_host_hermesc
|
||||
key: v2-hermesc-apple-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
|
||||
- name: Build HermesC Apple
|
||||
shell: bash
|
||||
run: |
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
. ./utils/build-apple-framework.sh
|
||||
build_host_hermesc_if_needed
|
||||
- name: Upload HermesC Artifact
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermesc-apple
|
||||
path: ./packages/react-native/sdks/hermes/build_host_hermesc
|
||||
- name: Cache hermesc apple
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, '-stable') }} # To avoid that the cache explode.
|
||||
with:
|
||||
path: ./packages/react-native/sdks/hermes/build_host_hermesc
|
||||
key: v2-hermesc-apple-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
|
||||
enableCrossOsArchive: true
|
||||
@@ -1,53 +0,0 @@
|
||||
name: build-hermesc-linux
|
||||
description: This action builds hermesc for linux platforms
|
||||
inputs:
|
||||
hermes-version:
|
||||
required: True
|
||||
description: The version of Hermes
|
||||
react-native-version:
|
||||
required: True
|
||||
description: The version of React Native
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y git openssh-client build-essential \
|
||||
libreadline-dev libicu-dev jq zip python3
|
||||
|
||||
# Install cmake 3.28.3-1build7
|
||||
sudo apt-get install cmake=3.28.3-1build7
|
||||
sudo ln -sf /usr/bin/cmake /usr/local/bin/cmake
|
||||
- name: Restore Hermes workspace
|
||||
uses: ./.github/actions/restore-hermes-workspace
|
||||
- name: Linux cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: v1-hermes-${{ github.job }}-linux-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
|
||||
path: |
|
||||
/tmp/hermes/linux64-bin/
|
||||
/tmp/hermes/hermes/destroot/
|
||||
- name: Set up workspace
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/hermes/linux64-bin
|
||||
- name: Build HermesC for Linux
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -f /tmp/hermes/linux64-bin/hermesc ]; then
|
||||
echo 'Skipping; Clean "/tmp/hermes/linux64-bin" to rebuild.'
|
||||
else
|
||||
cd /tmp/hermes
|
||||
cmake -S hermes -B build -DHERMES_STATIC_LINK=ON -DCMAKE_BUILD_TYPE=Release -DHERMES_ENABLE_TEST_SUITE=OFF \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True -DCMAKE_CXX_FLAGS=-s -DCMAKE_C_FLAGS=-s \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,--whole-archive -lpthread -Wl,--no-whole-archive"
|
||||
cmake --build build --target hermesc -j 4
|
||||
cp /tmp/hermes/build/bin/hermesc /tmp/hermes/linux64-bin/.
|
||||
fi
|
||||
- name: Upload linux artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermes-linux-bin
|
||||
path: /tmp/hermes/linux64-bin
|
||||
@@ -1,86 +0,0 @@
|
||||
name: build-hermesc-windows
|
||||
description: This action builds hermesc for Windows platforms
|
||||
inputs:
|
||||
hermes-version:
|
||||
required: True
|
||||
description: The version of Hermes
|
||||
react-native-version:
|
||||
required: True
|
||||
description: The version of React Native
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Download Previous Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-workspace
|
||||
path: 'C:\tmp\hermes'
|
||||
- name: Set up workspace
|
||||
shell: powershell
|
||||
run: |
|
||||
mkdir -p C:\tmp\hermes\osx-bin
|
||||
mkdir -p .\packages\react-native\sdks\hermes
|
||||
cp -r -Force C:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
|
||||
cp -r -Force .\packages\react-native\sdks\hermes-engine\utils\* .\packages\react-native\sdks\hermes\.
|
||||
- name: Windows cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: v3-hermes-${{ github.job }}-windows-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
|
||||
path: |
|
||||
C:\tmp\hermes\win64-bin\
|
||||
C:\tmp\hermes\hermes\icu\
|
||||
C:\tmp\hermes\hermes\deps\
|
||||
C:\tmp\hermes\hermes\build_release\
|
||||
- name: setup-msbuild
|
||||
uses: microsoft/setup-msbuild@v1.3.2
|
||||
- name: Set up workspace
|
||||
shell: powershell
|
||||
run: |
|
||||
New-Item -ItemType Directory -ErrorAction SilentlyContinue $Env:HERMES_WS_DIR\icu
|
||||
New-Item -ItemType Directory -ErrorAction SilentlyContinue $Env:HERMES_WS_DIR\deps
|
||||
New-Item -ItemType Directory -ErrorAction SilentlyContinue $Env:HERMES_WS_DIR\win64-bin
|
||||
- name: Downgrade CMake
|
||||
shell: powershell
|
||||
run: choco install cmake --version 3.31.6 --force
|
||||
- name: Build HermesC for Windows
|
||||
shell: powershell
|
||||
run: |
|
||||
if (-not(Test-Path -Path $Env:HERMES_WS_DIR\win64-bin\hermesc.exe)) {
|
||||
cd $Env:HERMES_WS_DIR\icu
|
||||
# If Invoke-WebRequest shows a progress bar, it will fail with
|
||||
# Win32 internal error "Access is denied" 0x5 occurred [...]
|
||||
$progressPreference = 'silentlyContinue'
|
||||
Invoke-WebRequest -Uri "$Env:ICU_URL" -OutFile "icu.zip"
|
||||
Expand-Archive -Path "icu.zip" -DestinationPath "."
|
||||
|
||||
cd $Env:HERMES_WS_DIR
|
||||
Copy-Item -Path "icu\bin64\icu*.dll" -Destination "deps"
|
||||
# Include MSVC++ 2015 redistributables
|
||||
Copy-Item -Path "c:\windows\system32\msvcp140.dll" -Destination "deps"
|
||||
Copy-Item -Path "c:\windows\system32\vcruntime140.dll" -Destination "deps"
|
||||
Copy-Item -Path "c:\windows\system32\vcruntime140_1.dll" -Destination "deps"
|
||||
|
||||
$Env:PATH += ";$Env:CMAKE_DIR;$Env:MSBUILD_DIR"
|
||||
$Env:ICU_ROOT = "$Env:HERMES_WS_DIR\icu"
|
||||
|
||||
cmake -S hermes -B build_release -G 'Visual Studio 17 2022' -Ax64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True -DHERMES_ENABLE_WIN10_ICU_FALLBACK=OFF
|
||||
if (-not $?) { throw "Failed to configure Hermes" }
|
||||
echo "Running windows build..."
|
||||
cd build_release
|
||||
cmake --build . --target hermesc --config Release
|
||||
if (-not $?) { throw "Failed to build Hermes" }
|
||||
|
||||
echo "Copying hermesc.exe to win64-bin"
|
||||
cd $Env:HERMES_WS_DIR
|
||||
Copy-Item -Path "build_release\bin\Release\hermesc.exe" -Destination "win64-bin"
|
||||
# Include Windows runtime dependencies
|
||||
Copy-Item -Path "deps\*" -Destination "win64-bin"
|
||||
}
|
||||
else {
|
||||
Write-Host "Skipping; Clean c:\tmp\hermes\win64-bin to rebuild."
|
||||
}
|
||||
- name: Upload windows artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermes-win64-bin
|
||||
path: C:\tmp\hermes\win64-bin\
|
||||
@@ -4,9 +4,6 @@ inputs:
|
||||
release-type:
|
||||
required: true
|
||||
description: The type of release we are building. It could be nightly, release or dry-run
|
||||
hermes-ws-dir:
|
||||
required: 'true'
|
||||
description: The workspace for hermes
|
||||
gha-npm-token:
|
||||
required: false
|
||||
description: The GHA npm token, required only to publish to npm
|
||||
@@ -20,87 +17,6 @@ runs:
|
||||
- name: Setup git safe folders
|
||||
shell: bash
|
||||
run: git config --global --add safe.directory '*'
|
||||
- name: Create /tmp/hermes/osx-bin directory
|
||||
shell: bash
|
||||
run: mkdir -p /tmp/hermes/osx-bin
|
||||
- name: Download osx-bin release artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-osx-bin-Release
|
||||
path: /tmp/hermes/osx-bin/Release
|
||||
- name: Download osx-bin debug artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-osx-bin-Debug
|
||||
path: /tmp/hermes/osx-bin/Debug
|
||||
- name: Download darwin-bin release artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-darwin-bin-Release
|
||||
path: /tmp/hermes/hermes-runtime-darwin
|
||||
- name: Download darwin-bin debug artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-darwin-bin-Debug
|
||||
path: /tmp/hermes/hermes-runtime-darwin
|
||||
- name: Download hermes dSYM debug artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-dSYM-Debug
|
||||
path: /tmp/hermes/dSYM/Debug
|
||||
- name: Download hermes dSYM release vartifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-dSYM-Release
|
||||
path: /tmp/hermes/dSYM/Release
|
||||
- name: Download windows-bin artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-win64-bin
|
||||
path: /tmp/hermes/win64-bin
|
||||
- name: Download linux-bin artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-linux-bin
|
||||
path: /tmp/hermes/linux64-bin
|
||||
- name: Show /tmp/hermes directory
|
||||
shell: bash
|
||||
run: ls -lR /tmp/hermes
|
||||
- name: Copy Hermes binaries
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ./packages/react-native/sdks/hermesc ./packages/react-native/sdks/hermesc/osx-bin ./packages/react-native/sdks/hermesc/win64-bin ./packages/react-native/sdks/hermesc/linux64-bin
|
||||
|
||||
# When build_hermes_macos runs as a matrix, it outputs
|
||||
if [[ -d ${{ inputs.hermes-ws-dir }}/osx-bin/Release ]]; then
|
||||
cp -r ${{ inputs.hermes-ws-dir }}/osx-bin/Release/* ./packages/react-native/sdks/hermesc/osx-bin/.
|
||||
elif [[ -d ${{ inputs.hermes-ws-dir }}/osx-bin/Debug ]]; then
|
||||
cp -r ${{ inputs.hermes-ws-dir }}/osx-bin/Debug/* ./packages/react-native/sdks/hermesc/osx-bin/.
|
||||
else
|
||||
ls ${{ inputs.hermes-ws-dir }}/osx-bin || echo "hermesc macOS artifacts directory missing."
|
||||
echo "Could not locate macOS hermesc binary."; exit 1;
|
||||
fi
|
||||
|
||||
# Sometimes, GHA creates artifacts with lowercase Debug/Release. Make sure that if it happen, we uppercase them.
|
||||
if [[ -f "${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-debug.tar.gz" ]]; then
|
||||
mv "${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-debug.tar.gz" "${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Debug.tar.gz"
|
||||
fi
|
||||
|
||||
if [[ -f "${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-release.tar.gz" ]]; then
|
||||
mv "${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-release.tar.gz" "${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Release.tar.gz"
|
||||
fi
|
||||
|
||||
cp -r ${{ inputs.hermes-ws-dir }}/win64-bin/* ./packages/react-native/sdks/hermesc/win64-bin/.
|
||||
cp -r ${{ inputs.hermes-ws-dir }}/linux64-bin/* ./packages/react-native/sdks/hermesc/linux64-bin/.
|
||||
|
||||
# Make sure the hermesc files are actually executable.
|
||||
chmod -R +x packages/react-native/sdks/hermesc/*
|
||||
|
||||
mkdir -p ./packages/react-native/ReactAndroid/external-artifacts/artifacts/
|
||||
cp ${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Debug.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-debug.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Release.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-release.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Debug/hermesvm.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-debug.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Release/hermesvm.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-release.tar.gz
|
||||
- name: Download ReactNativeDependencies
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
name: prepare-hermes-workspace
|
||||
description: This action prepares the hermes workspace with the right hermes and react-native versions.
|
||||
inputs:
|
||||
hermes-ws-dir:
|
||||
required: true
|
||||
description: The hermes dir we need to use to setup the workspace
|
||||
hermes-version-file:
|
||||
required: true
|
||||
description: the path to the file that will contain the hermes version
|
||||
outputs:
|
||||
hermes-version:
|
||||
description: the version of Hermes tied to this run
|
||||
value: ${{ steps.hermes-version.outputs.VERSION }}
|
||||
react-native-version:
|
||||
description: the version of React Native tied to this run
|
||||
value: ${{ steps.react-native-version.outputs.VERSION }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup hermes version
|
||||
shell: bash
|
||||
id: hermes-version
|
||||
run: |
|
||||
mkdir -p "/tmp/hermes" "/tmp/hermes/download" "/tmp/hermes/hermes"
|
||||
|
||||
if [ -f "${{ inputs.hermes-version-file }}" ]; then
|
||||
echo "Hermes Version file found! Using this version for the build:"
|
||||
echo "VERSION=$(cat ${{ inputs.hermes-version-file }})" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Hermes Version file not found!!!"
|
||||
echo "Using the last commit from main for the build:"
|
||||
HERMES_TAG_SHA=$(git ls-remote https://github.com/facebook/hermes main | cut -f 1 | tr -d '[:space:]')
|
||||
echo "VERSION=$HERMES_TAG_SHA" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
echo "Hermes commit is $HERMES_TAG_SHA"
|
||||
|
||||
- name: Get react-native version
|
||||
shell: bash
|
||||
id: react-native-version
|
||||
run: |
|
||||
VERSION=$(cat packages/react-native/package.json | jq -r '.version')
|
||||
# Save the react native version we are building in an output variable so we can use that file as part of the cache key.
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "React Native Version is $VERSION"
|
||||
|
||||
- name: Cache hermes workspace
|
||||
id: restore-hermes
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
/tmp/hermes/download/
|
||||
/tmp/hermes/hermes/
|
||||
key: v1-hermes-${{ steps.hermes-version.outputs.version }}
|
||||
enableCrossOsArchive: true
|
||||
|
||||
# It happened while testing that a cache was created from the right folders
|
||||
# but those folders where empty. Thus, the next check ensures that we can work with those caches.
|
||||
- name: Check if cache was meaningful
|
||||
id: meaningful-cache
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ -d /tmp/hermes/hermes ]] && [[ -n "$(ls -A /tmp/hermes/hermes)" ]]; then
|
||||
echo "Found a good hermes cache"
|
||||
echo "HERMES_CACHED=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Yarn- Install Dependencies
|
||||
if: ${{ steps.meaningful-cache.outputs.HERMES_CACHED != 'true' }}
|
||||
uses: ./.github/actions/yarn-install
|
||||
|
||||
- name: Download Hermes tarball
|
||||
if: ${{ steps.meaningful-cache.outputs.HERMES_CACHED != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
node packages/react-native/scripts/hermes/prepare-hermes-for-build ${{ github.event.pull_request.html_url }}
|
||||
cp packages/react-native/sdks/download/* ${{ inputs.hermes-ws-dir }}/download/.
|
||||
cp -r packages/react-native/sdks/hermes/* ${{ inputs.hermes-ws-dir }}/hermes/.
|
||||
|
||||
echo ${{ steps.hermes-version.outputs.version }}
|
||||
|
||||
- name: Upload Hermes artifact
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: hermes-workspace
|
||||
path: |
|
||||
/tmp/hermes/download/
|
||||
/tmp/hermes/hermes/
|
||||
|
||||
- name: Cache hermes workspace
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode.
|
||||
with:
|
||||
path: |
|
||||
/tmp/hermes/download/
|
||||
/tmp/hermes/hermes/
|
||||
key: v1-hermes-${{ steps.hermes-version.outputs.version }}
|
||||
enableCrossOsArchive: true
|
||||
@@ -1,16 +0,0 @@
|
||||
name: restore-hermes-workspace
|
||||
description: "Restore hermes workspace that has been created in Prepare Hermes Workspace"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Download Previous Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-workspace
|
||||
path: /tmp/hermes
|
||||
- name: Set up workspace
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HERMES_OSXBIN_ARTIFACTS_DIR ./packages/react-native/sdks/hermes
|
||||
cp -r $HERMES_WS_DIR/hermes/* ./packages/react-native/sdks/hermes/.
|
||||
cp -r ./packages/react-native/sdks/hermes-engine/utils ./packages/react-native/sdks/hermes/.
|
||||
@@ -5,6 +5,10 @@ inputs:
|
||||
description: 'The xcode version to use'
|
||||
required: false
|
||||
default: '16.2.0'
|
||||
platform:
|
||||
description: 'The platform to use. Valid values are: ios, ios-simulator, macos, mac-catalyst, tvos, tvos-simulator, xros, xros-simulator'
|
||||
required: false
|
||||
default: 'macos'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
@@ -12,3 +16,21 @@ runs:
|
||||
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
|
||||
with:
|
||||
xcode-version: ${{ inputs.xcode-version }}
|
||||
- name: Setup Platform ${{ inputs.platform }}
|
||||
if: ${{ inputs.platform != 'macos' && inputs.platform != 'mac-catalyst' }}
|
||||
shell: bash
|
||||
run: |
|
||||
# https://github.com/actions/runner-images/issues/12541
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
sudo xcrun simctl list
|
||||
|
||||
# Install platform based on the platform
|
||||
if [[ "${{ inputs.platform }}" == "xros" || "${{ inputs.platform }}" == "xros-simulator" ]]; then
|
||||
sudo xcodebuild -downloadPlatform visionOS
|
||||
elif [[ "${{ inputs.platform }}" == "tvos" || "${{ inputs.platform }}" == "tvos-simulator" ]]; then
|
||||
sudo xcodebuild -downloadPlatform tvOS
|
||||
else
|
||||
sudo xcodebuild -downloadPlatform iOS
|
||||
fi
|
||||
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
|
||||
@@ -10,36 +10,27 @@ inputs:
|
||||
flavor:
|
||||
description: The flavor of the build. Must be one of "Debug", "Release".
|
||||
default: Debug
|
||||
hermes-version:
|
||||
description: The version of hermes
|
||||
required: true
|
||||
react-native-version:
|
||||
description: The version of react-native
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
platform: ios
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Create Hermes folder
|
||||
shell: bash
|
||||
run: mkdir -p "$HERMES_WS_DIR"
|
||||
- name: Download Hermes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-darwin-bin-${{ inputs.flavor }}
|
||||
path: /tmp/hermes/hermes-runtime-darwin/
|
||||
- name: Print Downloaded hermes
|
||||
shell: bash
|
||||
run: ls -lR "$HERMES_WS_DIR"
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
- name: Set nightly Hermes versions
|
||||
shell: bash
|
||||
run: |
|
||||
node ./scripts/releases/use-hermes-nightly.js
|
||||
- name: Run yarn install again, with the correct hermes version
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Download ReactNativeDependencies
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -66,20 +57,6 @@ runs:
|
||||
args+=(--frameworks dynamic)
|
||||
fi
|
||||
|
||||
# Tarball is restored with capital flavors suffix, but somehow the tarball name from JS at line 96 returns as lowercased.
|
||||
# Let's ensure that the tarballs have the right names
|
||||
|
||||
if [[ -f "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Debug.tar.gz" ]]; then
|
||||
mv "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Debug.tar.gz" "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-debug.tar.gz"
|
||||
fi
|
||||
|
||||
if [[ -f "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Release.tar.gz" ]]; then
|
||||
mv "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Release.tar.gz" "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-release.tar.gz"
|
||||
fi
|
||||
|
||||
BUILD_TYPE="${{ inputs.flavor }}"
|
||||
TARBALL_FILENAME=$(node ../../packages/react-native/scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE")
|
||||
export HERMES_ENGINE_TARBALL_PATH="$HERMES_WS_DIR/hermes-runtime-darwin/$TARBALL_FILENAME"
|
||||
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
|
||||
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ inputs.flavor }}.xcframework.tar.gz"
|
||||
|
||||
|
||||
@@ -10,18 +10,9 @@ inputs:
|
||||
run-unit-tests:
|
||||
description: whether unit tests should run or not.
|
||||
default: "false"
|
||||
hermes-tarball-artifacts-dir:
|
||||
description: The directory where the hermes tarball artifacts are stored
|
||||
default: /tmp/hermes/hermes-runtime-darwin
|
||||
flavor:
|
||||
description: The flavor of the build. Must be one of "Debug", "Release".
|
||||
default: Debug
|
||||
hermes-version:
|
||||
description: The version of hermes
|
||||
required: true
|
||||
react-native-version:
|
||||
description: The version of react-native
|
||||
required: true
|
||||
run-e2e-tests:
|
||||
description: Whether we want to run E2E tests or not
|
||||
required: false
|
||||
@@ -32,60 +23,25 @@ runs:
|
||||
steps:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
platform: ios
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Run yarn
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Download Hermes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-darwin-bin-${{ inputs.flavor }}
|
||||
path: ${{ inputs.hermes-tarball-artifacts-dir }}
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
- name: Set nightly Hermes versions
|
||||
shell: bash
|
||||
run: |
|
||||
node ./scripts/releases/use-hermes-nightly.js
|
||||
- name: Run yarn install again, with the correct hermes version
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Prepare IOS Tests
|
||||
if: ${{ inputs.run-unit-tests == 'true' }}
|
||||
uses: ./.github/actions/prepare-ios-tests
|
||||
- name: Set HERMES_ENGINE_TARBALL_PATH envvar if Hermes tarball is present
|
||||
shell: bash
|
||||
run: |
|
||||
HERMES_TARBALL_ARTIFACTS_DIR=${{ inputs.hermes-tarball-artifacts-dir }}
|
||||
if [ ! -d $HERMES_TARBALL_ARTIFACTS_DIR ]; then
|
||||
echo "Hermes tarball artifacts dir not present ($HERMES_TARBALL_ARTIFACTS_DIR). Build Hermes from source."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "${{ inputs.flavor }}")
|
||||
TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME
|
||||
|
||||
echo "Looking for $TARBALL_FILENAME in $HERMES_TARBALL_ARTIFACTS_DIR"
|
||||
echo "$TARBALL_PATH"
|
||||
|
||||
if [ ! -f $TARBALL_PATH ]; then
|
||||
echo "Hermes tarball not present ($TARBALL_PATH). Build Hermes from source."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Found Hermes tarball at $TARBALL_PATH"
|
||||
echo "HERMES_ENGINE_TARBALL_PATH=$TARBALL_PATH" >> $GITHUB_ENV
|
||||
- name: Print Hermes version
|
||||
shell: bash
|
||||
run: |
|
||||
HERMES_TARBALL_ARTIFACTS_DIR=${{ inputs.hermes-tarball-artifacts-dir }}
|
||||
TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "${{ inputs.flavor }}")
|
||||
TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME
|
||||
if [[ -e $TARBALL_PATH ]]; then
|
||||
tar -xf $TARBALL_PATH
|
||||
echo 'print(HermesInternal?.getRuntimeProperties?.()["OSS Release Version"])' > test.js
|
||||
chmod +x ./destroot/bin/hermes
|
||||
./destroot/bin/hermes test.js
|
||||
rm test.js
|
||||
rm -rf destroot
|
||||
else
|
||||
echo 'No Hermes tarball found.'
|
||||
fi
|
||||
- name: Download ReactNativeDependencies
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -105,7 +61,6 @@ runs:
|
||||
- name: Install CocoaPods dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
export HERMES_ENGINE_TARBALL_PATH=$HERMES_ENGINE_TARBALL_PATH
|
||||
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
|
||||
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ inputs.flavor }}.xcframework.tar.gz"
|
||||
|
||||
@@ -116,7 +71,7 @@ runs:
|
||||
cd packages/rn-tester
|
||||
|
||||
bundle install
|
||||
bundle exec pod install
|
||||
bundle exec pod update hermes-engine --no-repo-update
|
||||
- name: Build RNTester
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -121,7 +121,7 @@ describe('Create Draft Release', () => {
|
||||
});
|
||||
|
||||
describe('#_computeBody', () => {
|
||||
it('computes body for release', async () => {
|
||||
it('computes body for release when no hermes versions are passed', async () => {
|
||||
const version = '0.77.1';
|
||||
const changelog = `## v${version}
|
||||
### Breaking Changes
|
||||
@@ -134,20 +134,83 @@ describe('Create Draft Release', () => {
|
||||
#### iOS
|
||||
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
|
||||
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change`;
|
||||
const body = _computeBody(version, changelog);
|
||||
const body = _computeBody(changelog, version);
|
||||
|
||||
expect(body).toEqual(`${changelog}
|
||||
|
||||
---
|
||||
|
||||
Hermes dSYMS:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-release.tar.gz)
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-release.tar.gz)
|
||||
|
||||
Hermes V1 dSYMS:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-release.tar.gz)
|
||||
|
||||
ReactNativeDependencies dSYMs:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
|
||||
|
||||
ReactNative Core dSYMs:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-release.tar.gz)
|
||||
|
||||
---
|
||||
|
||||
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
|
||||
|
||||
---
|
||||
|
||||
To help you upgrade to this version, you can use the [Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) ⚛️.
|
||||
|
||||
---
|
||||
|
||||
View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/react-native/blob/main/CHANGELOG.md).`);
|
||||
});
|
||||
|
||||
it('computes body for release when hermes versions are passed', async () => {
|
||||
const version = '0.77.1';
|
||||
const hermesVersion = '0.15.0';
|
||||
const hermesV1Version = '250829098.0.2';
|
||||
const changelog = `## v${version}
|
||||
### Breaking Changes
|
||||
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
|
||||
|
||||
#### Android
|
||||
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
|
||||
- [PR #3457](https://github.com/facebook/react-native/pull/3457) - Some other change
|
||||
|
||||
#### iOS
|
||||
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
|
||||
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change`;
|
||||
const body = _computeBody(
|
||||
changelog,
|
||||
version,
|
||||
hermesVersion,
|
||||
hermesV1Version,
|
||||
);
|
||||
|
||||
expect(body).toEqual(`${changelog}
|
||||
|
||||
---
|
||||
|
||||
Hermes dSYMS:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-release.tar.gz)
|
||||
|
||||
Hermes V1 dSYMS:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-release.tar.gz)
|
||||
|
||||
ReactNativeDependencies dSYMs:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
|
||||
|
||||
ReactNative Core dSYMs:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-release.tar.gz)
|
||||
|
||||
---
|
||||
|
||||
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
|
||||
|
||||
@@ -40,19 +40,29 @@ function _extractChangelog(version) {
|
||||
return changelog.slice(changelogStarts, changelogEnds).join('\n').trim();
|
||||
}
|
||||
|
||||
function _computeBody(version, changelog) {
|
||||
function _computeBody(changelog, version, hermesVersion, hermesV1Version) {
|
||||
hermesVersion = hermesVersion ?? version;
|
||||
hermesV1Version = hermesV1Version ?? version;
|
||||
return `${changelog}
|
||||
|
||||
---
|
||||
|
||||
Hermes dSYMS:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-release.tar.gz)
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-release.tar.gz)
|
||||
|
||||
Hermes V1 dSYMS:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-release.tar.gz)
|
||||
|
||||
ReactNativeDependencies dSYMs:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
|
||||
|
||||
ReactNative Core dSYMs:
|
||||
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-debug.tar.gz)
|
||||
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-release.tar.gz)
|
||||
|
||||
---
|
||||
|
||||
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
|
||||
@@ -113,7 +123,13 @@ function moveToChangelogBranch(version) {
|
||||
run(`git checkout -b changelog/v${version}`);
|
||||
}
|
||||
|
||||
async function createDraftRelease(version, latest, token) {
|
||||
async function createDraftRelease(
|
||||
version,
|
||||
latest,
|
||||
token,
|
||||
hermesVersion,
|
||||
hermesV1Version,
|
||||
) {
|
||||
if (version.startsWith('v')) {
|
||||
version = version.substring(1);
|
||||
}
|
||||
@@ -121,7 +137,7 @@ async function createDraftRelease(version, latest, token) {
|
||||
_verifyTagExists(version);
|
||||
moveToChangelogBranch(version);
|
||||
const changelog = _extractChangelog(version);
|
||||
const body = _computeBody(version, changelog);
|
||||
const body = _computeBody(changelog, version, hermesVersion, hermesV1Version);
|
||||
const release = await _createDraftReleaseOnGitHub(
|
||||
version,
|
||||
body,
|
||||
|
||||
@@ -162,7 +162,7 @@ async function main() {
|
||||
console.info(`WORKING_DIRECTORY: ${WORKING_DIRECTORY}`);
|
||||
console.info('==============================\n');
|
||||
|
||||
const simulatorName = 'iPhone 15 Pro';
|
||||
const simulatorName = 'iPhone 16 Pro';
|
||||
launchSimulator(simulatorName);
|
||||
installAppOnSimulator(APP_PATH);
|
||||
const udid = extractSimulatorUDID();
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
xcode-version: '16.2.0'
|
||||
platform: 'ios'
|
||||
- name: Extract branch name
|
||||
run: |
|
||||
TAG="${{ github.ref_name }}";
|
||||
|
||||
@@ -2,6 +2,15 @@ name: Create Draft Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
hermesVersion:
|
||||
required: false
|
||||
type: string
|
||||
description: The version of Hermes to use for this release (eg. 0.15.0). If not specified, it will use React Native Version
|
||||
hermesV1Version:
|
||||
required: false
|
||||
type: string
|
||||
description: The version of Hermes V1 to use for this release (eg. 250829098.0.2). If not specified, it will use React Native Version
|
||||
|
||||
jobs:
|
||||
create-draft-release:
|
||||
@@ -27,7 +36,7 @@ jobs:
|
||||
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
|
||||
const version = '${{ github.ref_name }}';
|
||||
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
|
||||
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}')).id;
|
||||
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}', ${{ inputs.hermesVersion }}, ${{ inputs.hermesV1Version }})).id;
|
||||
result-encoding: string
|
||||
- name: Upload release assets for DotSlash
|
||||
uses: actions/github-script@v6
|
||||
|
||||
@@ -21,86 +21,6 @@ jobs:
|
||||
echo "Setting release type to nightly"
|
||||
echo "RELEASE_TYPE=nightly" >> $GITHUB_OUTPUT
|
||||
|
||||
prepare_hermes_workspace:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_VERSION_FILE: packages/react-native/sdks/.hermesversion
|
||||
outputs:
|
||||
react-native-version: ${{ steps.prepare-hermes-workspace.outputs.react-native-version }}
|
||||
hermes-version: ${{ steps.prepare-hermes-workspace.outputs.hermes-version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare Hermes Workspace
|
||||
id: prepare-hermes-workspace
|
||||
uses: ./.github/actions/prepare-hermes-workspace
|
||||
with:
|
||||
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
|
||||
hermes-version-file: ${{ env.HERMES_VERSION_FILE }}
|
||||
|
||||
build_hermesc_apple:
|
||||
runs-on: macos-14
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Apple
|
||||
uses: ./.github/actions/build-hermesc-apple
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_apple_slices_hermes:
|
||||
runs-on: macos-14
|
||||
needs: [build_hermesc_apple, prepare_hermes_workspace]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
HERMES_OSXBIN_ARTIFACTS_DIR: /tmp/hermes/osx-bin
|
||||
IOS_DEPLOYMENT_TARGET: "15.1"
|
||||
XROS_DEPLOYMENT_TARGET: "1.0"
|
||||
MAC_DEPLOYMENT_TARGET: "10.15"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
slice: [macosx, iphoneos, iphonesimulator, appletvos, appletvsimulator, catalyst, xros, xrsimulator]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Slice
|
||||
uses: ./.github/actions/build-apple-slices-hermes
|
||||
with:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
slice: ${{ matrix.slice }}
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_hermes_macos:
|
||||
runs-on: macos-14
|
||||
needs: [build_apple_slices_hermes, prepare_hermes_workspace]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Hermes MacOS
|
||||
uses: ./.github/actions/build-hermes-macos
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
prebuild_apple_dependencies:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
uses: ./.github/workflows/prebuild-ios-dependencies.yml
|
||||
@@ -108,42 +28,10 @@ jobs:
|
||||
|
||||
prebuild_react_native_core:
|
||||
uses: ./.github/workflows/prebuild-ios-core.yml
|
||||
with:
|
||||
use-hermes-nightly: true
|
||||
secrets: inherit
|
||||
needs: [prebuild_apple_dependencies, build_hermes_macos]
|
||||
|
||||
build_hermesc_linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Linux
|
||||
uses: ./.github/actions/build-hermesc-linux
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_hermesc_windows:
|
||||
runs-on: windows-2025
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: 'C:\tmp\hermes'
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
|
||||
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
|
||||
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
|
||||
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
|
||||
CMAKE_DIR: 'C:\Program Files\CMake\bin'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Windows
|
||||
uses: ./.github/actions/build-hermesc-windows
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
needs: [prebuild_apple_dependencies]
|
||||
|
||||
build_android:
|
||||
runs-on: 8-core-ubuntu
|
||||
@@ -172,10 +60,6 @@ jobs:
|
||||
needs:
|
||||
[
|
||||
set_release_type,
|
||||
prepare_hermes_workspace,
|
||||
build_hermes_macos,
|
||||
build_hermesc_linux,
|
||||
build_hermesc_windows,
|
||||
build_android,
|
||||
prebuild_apple_dependencies,
|
||||
prebuild_react_native_core,
|
||||
@@ -188,7 +72,6 @@ jobs:
|
||||
# By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs.
|
||||
ORG_GRADLE_PROJECT_reactNativeArchitectures: "arm64-v8a"
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }}
|
||||
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
|
||||
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
|
||||
@@ -200,7 +83,6 @@ jobs:
|
||||
- name: Build and Publish NPM Package
|
||||
uses: ./.github/actions/build-npm-package
|
||||
with:
|
||||
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
|
||||
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
|
||||
gha-npm-token: ${{ env.GHA_NPM_TOKEN }}
|
||||
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
|
||||
|
||||
@@ -2,11 +2,16 @@ name: Prebuild iOS Dependencies
|
||||
|
||||
on:
|
||||
workflow_call: # this directive allow us to call this workflow from other workflows
|
||||
|
||||
inputs:
|
||||
use-hermes-nightly:
|
||||
description: 'Whether to use the hermes nightly build or read the version from the versions.properties file'
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build-rn-slice:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -32,39 +37,20 @@ jobs:
|
||||
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
xcode-version: '16.2.0'
|
||||
platform: ${{ matrix.slice }}
|
||||
- name: Yarn Install
|
||||
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Download Hermes
|
||||
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-darwin-bin-${{ matrix.flavor }}
|
||||
path: /tmp/hermes/hermes-runtime-darwin
|
||||
- name: Extract Hermes
|
||||
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
|
||||
- name: Set Hermes version
|
||||
shell: bash
|
||||
run: |
|
||||
HERMES_TARBALL_ARTIFACTS_DIR=/tmp/hermes/hermes-runtime-darwin
|
||||
if [ ! -d $HERMES_TARBALL_ARTIFACTS_DIR ]; then
|
||||
echo "Hermes tarball artifacts dir not present ($HERMES_TARBALL_ARTIFACTS_DIR)."
|
||||
exit 0
|
||||
if [ "${{ inputs.use-hermes-nightly }}" == "true" ]; then
|
||||
HERMES_VERSION="nightly"
|
||||
else
|
||||
HERMES_VERSION=$(sed -n 's/^HERMES_VERSION_NAME=//p' packages/react-native/sdks/hermes-engine/version.properties)
|
||||
fi
|
||||
|
||||
TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "${{ matrix.flavor }}")
|
||||
TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME
|
||||
|
||||
echo "Looking for $TARBALL_FILENAME in $HERMES_TARBALL_ARTIFACTS_DIR"
|
||||
echo "$TARBALL_PATH"
|
||||
|
||||
if [ ! -f $TARBALL_PATH ]; then
|
||||
echo "Hermes tarball not present ($TARBALL_PATH). Build Hermes from source."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Found Hermes tarball at $TARBALL_PATH"
|
||||
echo "HERMES_ENGINE_TARBALL_PATH=$TARBALL_PATH" >> $GITHUB_ENV
|
||||
echo "Using Hermes version: $HERMES_VERSION"
|
||||
echo "HERMES_VERSION=$HERMES_VERSION" >> $GITHUB_ENV
|
||||
- name: Download ReactNativeDependencies
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -123,7 +109,7 @@ jobs:
|
||||
packages/react-native/.build/headers
|
||||
|
||||
compose-xcframework:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
needs: [build-rn-slice]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
prepare_workspace:
|
||||
name: Prepare workspace
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: packages/react-native/third-party/
|
||||
key: v2-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
key: v3-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
enableCrossOsArchive: true
|
||||
- name: Yarn Install
|
||||
if: steps.restore-ios-prebuilds.outputs.cache-hit != 'true'
|
||||
@@ -40,13 +40,13 @@ jobs:
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
|
||||
with:
|
||||
key: v2-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
key: v3-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
enableCrossOsArchive: true
|
||||
path: packages/react-native/third-party/
|
||||
|
||||
build-apple-slices:
|
||||
name: Build Apple Slice
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
needs: [prepare_workspace]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -65,16 +65,17 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
xcode-version: '16.1'
|
||||
- name: Restore slice folder
|
||||
id: restore-slice-folder
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: packages/react-native/third-party/.build/Build/Products
|
||||
key: v2-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
key: v3-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
- name: Setup xcode
|
||||
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
platform: ${{ matrix.slice }}
|
||||
- name: Yarn Install
|
||||
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/yarn-install
|
||||
@@ -85,15 +86,8 @@ jobs:
|
||||
name: ios-prebuilds-workspace
|
||||
path: packages/react-native/third-party/
|
||||
- name: Print third-party folder structure
|
||||
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
|
||||
run: ls -lR packages/react-native/third-party
|
||||
- name: Install VisionOS
|
||||
if: ${{ steps.restore-slice-folder.outputs.cache-hit != 'true' && (matrix.slice == 'xros' || matrix.slice == 'xros-simulator') }}
|
||||
run: |
|
||||
# https://github.com/actions/runner-images/issues/10559
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
sudo xcrun simctl list
|
||||
sudo xcodebuild -downloadPlatform visionOS
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
- name: Build slice ${{ matrix.slice }} for ${{ matrix.flavor }}
|
||||
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
|
||||
run: node scripts/releases/prepare-ios-prebuilds.js -b -p ${{ matrix.slice }} -r ${{ matrix.flavor }}
|
||||
@@ -107,14 +101,14 @@ jobs:
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
|
||||
with:
|
||||
key: v2-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
key: v3-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
enableCrossOsArchive: true
|
||||
path: |
|
||||
packages/react-native/third-party/.build/Build/Products
|
||||
|
||||
create-xcframework:
|
||||
name: Prepare XCFramework
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
needs: [build-apple-slices]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -138,7 +132,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
packages/react-native/third-party/
|
||||
key: v2-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
key: v3-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
# If cache hit, we already have our binary. We don't need to do anything.
|
||||
- name: Yarn Install
|
||||
if: steps.restore-xcframework.outputs.cache-hit != 'true'
|
||||
@@ -200,4 +194,4 @@ jobs:
|
||||
path: |
|
||||
packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
|
||||
packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
|
||||
key: v2-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
key: v3-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
|
||||
|
||||
@@ -19,84 +19,27 @@ jobs:
|
||||
echo "Setting release type to release"
|
||||
echo "RELEASE_TYPE=release" >> $GITHUB_OUTPUT
|
||||
|
||||
prepare_hermes_workspace:
|
||||
set_hermes_versions:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_VERSION_FILE: packages/react-native/sdks/.hermesversion
|
||||
outputs:
|
||||
react-native-version: ${{ steps.prepare-hermes-workspace.outputs.react-native-version }}
|
||||
hermes-version: ${{ steps.prepare-hermes-workspace.outputs.hermes-version }}
|
||||
HERMES_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}
|
||||
HERMES_V1_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare Hermes Workspace
|
||||
id: prepare-hermes-workspace
|
||||
uses: ./.github/actions/prepare-hermes-workspace
|
||||
with:
|
||||
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
|
||||
hermes-version-file: ${{ env.HERMES_VERSION_FILE }}
|
||||
- id: set_hermes_versions
|
||||
run: |
|
||||
echo "Setting hermes versions to latest"
|
||||
hermes_version=$(grep -oE 'HERMES_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2)
|
||||
hermes_v1_version=$(grep -oE 'HERMES_V1_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2)
|
||||
|
||||
build_hermesc_apple:
|
||||
runs-on: macos-14
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Apple
|
||||
uses: ./.github/actions/build-hermesc-apple
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.output.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.output.react-native-version }}
|
||||
build_apple_slices_hermes:
|
||||
runs-on: macos-14
|
||||
needs: [build_hermesc_apple, prepare_hermes_workspace]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
HERMES_OSXBIN_ARTIFACTS_DIR: /tmp/hermes/osx-bin
|
||||
IOS_DEPLOYMENT_TARGET: "15.1"
|
||||
XROS_DEPLOYMENT_TARGET: "1.0"
|
||||
MAC_DEPLOYMENT_TARGET: "10.15"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
slice: [macosx, iphoneos, iphonesimulator, appletvos, appletvsimulator, catalyst, xros, xrsimulator]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Slice
|
||||
uses: ./.github/actions/build-apple-slices-hermes
|
||||
with:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
slice: ${{ matrix.slice}}
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_hermes_macos:
|
||||
runs-on: macos-14
|
||||
needs: [build_apple_slices_hermes, prepare_hermes_workspace]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Hermes MacOS
|
||||
uses: ./.github/actions/build-hermes-macos
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
flavor: ${{ matrix.flavor }}
|
||||
echo "HERMES_VERSION=$hermes_version" >> $GITHUB_OUTPUT
|
||||
echo "HERMES_V1_VERSION=$hermes_v1_version" >> $GITHUB_OUTPUT
|
||||
- name: Print hermes versions
|
||||
run: |
|
||||
echo "HERMES_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}"
|
||||
echo "HERMES_V1_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}"
|
||||
|
||||
prebuild_apple_dependencies:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
@@ -106,51 +49,13 @@ jobs:
|
||||
prebuild_react_native_core:
|
||||
uses: ./.github/workflows/prebuild-ios-core.yml
|
||||
secrets: inherit
|
||||
needs: [prebuild_apple_dependencies, build_hermes_macos]
|
||||
|
||||
build_hermesc_linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Linux
|
||||
uses: ./.github/actions/build-hermesc-linux
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_hermesc_windows:
|
||||
runs-on: windows-2025
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: 'C:\tmp\hermes'
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
|
||||
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
|
||||
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
|
||||
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
|
||||
CMAKE_DIR: 'C:\Program Files\CMake\bin'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Windows
|
||||
uses: ./.github/actions/build-hermesc-windows
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
needs: [prebuild_apple_dependencies]
|
||||
|
||||
build_npm_package:
|
||||
runs-on: 8-core-ubuntu
|
||||
needs:
|
||||
[
|
||||
set_release_type,
|
||||
prepare_hermes_workspace,
|
||||
build_hermes_macos,
|
||||
build_hermesc_linux,
|
||||
build_hermesc_windows,
|
||||
prebuild_apple_dependencies,
|
||||
prebuild_react_native_core,
|
||||
]
|
||||
@@ -162,7 +67,6 @@ jobs:
|
||||
# By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs.
|
||||
ORG_GRADLE_PROJECT_reactNativeArchitectures: "arm64-v8a"
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }}
|
||||
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
|
||||
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
|
||||
@@ -178,7 +82,6 @@ jobs:
|
||||
- name: Build and Publish NPM Package
|
||||
uses: ./.github/actions/build-npm-package
|
||||
with:
|
||||
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
|
||||
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
|
||||
gha-npm-token: ${{ env.GHA_NPM_TOKEN }}
|
||||
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
|
||||
@@ -236,6 +139,9 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
create_draft_release:
|
||||
needs: generate_changelog
|
||||
needs: [generate_changelog, set_hermes_versions]
|
||||
uses: ./.github/workflows/create-draft-release.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
hermesVersion: ${{ needs.set_hermes_versions.outputs.HERMES_VERSION }}
|
||||
hermesV1Version: ${{ needs.set_hermes_versions.outputs.HERMES_V1_VERSION }}
|
||||
|
||||
+24
-163
@@ -33,86 +33,6 @@ jobs:
|
||||
|
||||
echo "Should I run E2E tests? ${{ inputs.run-e2e-tests }}"
|
||||
|
||||
prepare_hermes_workspace:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_VERSION_FILE: packages/react-native/sdks/.hermesversion
|
||||
outputs:
|
||||
react-native-version: ${{ steps.prepare-hermes-workspace.outputs.react-native-version }}
|
||||
hermes-version: ${{ steps.prepare-hermes-workspace.outputs.hermes-version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare Hermes Workspace
|
||||
id: prepare-hermes-workspace
|
||||
uses: ./.github/actions/prepare-hermes-workspace
|
||||
with:
|
||||
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
|
||||
hermes-version-file: ${{ env.HERMES_VERSION_FILE }}
|
||||
|
||||
build_hermesc_apple:
|
||||
runs-on: macos-14
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Apple
|
||||
uses: ./.github/actions/build-hermesc-apple
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_apple_slices_hermes:
|
||||
runs-on: macos-14
|
||||
needs: [build_hermesc_apple, prepare_hermes_workspace]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
HERMES_OSXBIN_ARTIFACTS_DIR: /tmp/hermes/osx-bin
|
||||
IOS_DEPLOYMENT_TARGET: "15.1"
|
||||
XROS_DEPLOYMENT_TARGET: "1.0"
|
||||
MAC_DEPLOYMENT_TARGET: "10.15"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
slice: [macosx, iphoneos, iphonesimulator, appletvos, appletvsimulator, catalyst, xros, xrsimulator]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Slice
|
||||
uses: ./.github/actions/build-apple-slices-hermes
|
||||
with:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
slice: ${{ matrix.slice}}
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_hermes_macos:
|
||||
runs-on: macos-14
|
||||
needs: [build_apple_slices_hermes, prepare_hermes_workspace]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Hermes MacOS
|
||||
uses: ./.github/actions/build-hermes-macos
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
prebuild_apple_dependencies:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
uses: ./.github/workflows/prebuild-ios-dependencies.yml
|
||||
@@ -120,16 +40,15 @@ jobs:
|
||||
|
||||
prebuild_react_native_core:
|
||||
uses: ./.github/workflows/prebuild-ios-core.yml
|
||||
with:
|
||||
use-hermes-nightly: ${{ !endsWith(github.ref_name, '-stable') }}
|
||||
secrets: inherit
|
||||
needs: [prebuild_apple_dependencies, build_hermes_macos]
|
||||
needs: [prebuild_apple_dependencies]
|
||||
|
||||
test_ios_rntester_ruby_3_2_0:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
needs:
|
||||
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies, prebuild_react_native_core]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
[prebuild_apple_dependencies, prebuild_react_native_core]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -137,17 +56,12 @@ jobs:
|
||||
uses: ./.github/actions/test-ios-rntester
|
||||
with:
|
||||
ruby-version: "3.2.0"
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
flavor: Debug
|
||||
|
||||
test_ios_rntester:
|
||||
runs-on: macos-14-large
|
||||
runs-on: macos-15-large
|
||||
needs:
|
||||
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies, prebuild_react_native_core]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
[prebuild_apple_dependencies, prebuild_react_native_core]
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -161,17 +75,12 @@ jobs:
|
||||
uses: ./.github/actions/test-ios-rntester
|
||||
with:
|
||||
use-frameworks: ${{ matrix.frameworks }}
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
test_e2e_ios_rntester:
|
||||
runs-on: macos-14-large
|
||||
runs-on: macos-15-large
|
||||
needs:
|
||||
[test_ios_rntester]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -190,6 +99,8 @@ jobs:
|
||||
run: ls -lR /tmp/RNTesterBuild
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
platform: ios
|
||||
- name: Run E2E Tests
|
||||
uses: ./.github/actions/maestro-ios
|
||||
with:
|
||||
@@ -199,11 +110,8 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
test_e2e_ios_templateapp:
|
||||
runs-on: macos-14-large
|
||||
runs-on: macos-15-large
|
||||
needs: [build_npm_package, prebuild_apple_dependencies]
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -213,6 +121,8 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
with:
|
||||
platform: ios
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Run yarn
|
||||
@@ -221,11 +131,6 @@ jobs:
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6.10
|
||||
- name: Download Hermes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: hermes-darwin-bin-${{matrix.flavor}}
|
||||
path: /tmp/react-native-tmp
|
||||
- name: Download React Native Package
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -259,9 +164,6 @@ jobs:
|
||||
REACT_NATIVE_PKG=$(find /tmp/react-native-tmp -type f -name "*.tgz")
|
||||
echo "React Native tgs is $REACT_NATIVE_PKG"
|
||||
|
||||
HERMES_PATH=$(find /tmp/react-native-tmp -type f -name "*.tar.gz")
|
||||
echo "Hermes path is $HERMES_PATH"
|
||||
|
||||
# For stable branches, we want to use the stable branch of the template
|
||||
# In all the other cases, we want to use "main"
|
||||
BRANCH=${{ github.ref_name }}
|
||||
@@ -278,7 +180,7 @@ jobs:
|
||||
export RCT_USE_LOCAL_RN_DEP=/tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
|
||||
# Disable prebuilds for now, as they are causing issues with E2E tests for 0.82-stable branch
|
||||
# export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
|
||||
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
|
||||
RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
|
||||
|
||||
xcodebuild \
|
||||
-scheme "RNTestProject" \
|
||||
@@ -364,21 +266,6 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
working-directory: /tmp/RNTestProject
|
||||
|
||||
build_hermesc_linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Linux
|
||||
uses: ./.github/actions/build-hermesc-linux
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
run_fantom_tests:
|
||||
runs-on: 8-core-ubuntu
|
||||
needs: [set_release_type]
|
||||
@@ -398,25 +285,6 @@ jobs:
|
||||
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
|
||||
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
|
||||
|
||||
build_hermesc_windows:
|
||||
runs-on: windows-2025
|
||||
needs: prepare_hermes_workspace
|
||||
env:
|
||||
HERMES_WS_DIR: 'C:\tmp\hermes'
|
||||
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
|
||||
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
|
||||
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
|
||||
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
|
||||
CMAKE_DIR: 'C:\Program Files\CMake\bin'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build HermesC Windows
|
||||
uses: ./.github/actions/build-hermesc-windows
|
||||
with:
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
build_android:
|
||||
runs-on: 8-core-ubuntu
|
||||
needs: [set_release_type]
|
||||
@@ -471,10 +339,6 @@ jobs:
|
||||
needs:
|
||||
[
|
||||
set_release_type,
|
||||
prepare_hermes_workspace,
|
||||
build_hermes_macos,
|
||||
build_hermesc_linux,
|
||||
build_hermesc_windows,
|
||||
build_android,
|
||||
prebuild_apple_dependencies,
|
||||
prebuild_react_native_core,
|
||||
@@ -484,15 +348,12 @@ jobs:
|
||||
env:
|
||||
TERM: "dumb"
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
env:
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build NPM Package
|
||||
uses: ./.github/actions/build-npm-package
|
||||
with:
|
||||
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
|
||||
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
|
||||
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
|
||||
|
||||
@@ -535,6 +396,12 @@ jobs:
|
||||
cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Set nightly Hermes versions
|
||||
shell: bash
|
||||
run: |
|
||||
node ./scripts/releases/use-hermes-nightly.js
|
||||
- name: Run yarn install again, with the correct hermes version
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Prepare the Helloworld application
|
||||
shell: bash
|
||||
run: node ./scripts/e2e/init-project-e2e.js --useHelloWorld --pathToLocalReactNative "$GITHUB_WORKSPACE/build/$(cat build/react-native-package-version)"
|
||||
@@ -555,11 +422,10 @@ jobs:
|
||||
compression-level: 0
|
||||
|
||||
test_ios_helloworld_with_ruby_3_2_0:
|
||||
runs-on: macos-14
|
||||
needs: [prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies, prebuild_react_native_core] # prepare_hermes_workspace must be there because we need its reference to retrieve a couple of outputs
|
||||
runs-on: macos-15
|
||||
needs: [prebuild_apple_dependencies, prebuild_react_native_core]
|
||||
env:
|
||||
PROJECT_NAME: iOSTemplateProject
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
YARN_ENABLE_IMMUTABLE_INSTALLS: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -568,12 +434,10 @@ jobs:
|
||||
with:
|
||||
ruby-version: 3.2.0
|
||||
flavor: Debug
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
test_ios_helloworld:
|
||||
runs-on: macos-14
|
||||
needs: [prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies, prebuild_react_native_core] # prepare_hermes_workspace must be there because we need its reference to retrieve a couple of outputs
|
||||
runs-on: macos-15
|
||||
needs: [prebuild_apple_dependencies, prebuild_react_native_core]
|
||||
strategy:
|
||||
matrix:
|
||||
flavor: [Debug, Release]
|
||||
@@ -584,7 +448,6 @@ jobs:
|
||||
use_frameworks: StaticLibraries
|
||||
env:
|
||||
PROJECT_NAME: iOSTemplateProject
|
||||
HERMES_WS_DIR: /tmp/hermes
|
||||
YARN_ENABLE_IMMUTABLE_INSTALLS: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -593,8 +456,6 @@ jobs:
|
||||
with:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
use-frameworks: ${{ matrix.use_frameworks }}
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
test_js:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Changelog (pre 0.80)
|
||||
|
||||
## v0.79.7
|
||||
|
||||
### Fixed
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Layout:** Make missing parent view state in updateLayout a soft error ([56ad8d9bfd](https://github.com/facebook/react-native/commit/56ad8d9bfd08ea70bc6f5726a2b4a6afb6d2d7c2) by [@javache](https://github.com/javache))
|
||||
- **Layout:** Make missing parent view state in updateLayout a soft error ([f2e47d8dab](https://github.com/facebook/react-native/commit/f2e47d8dabcd61621ea81c86cd1e2488948c4229) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
|
||||
## v0.79.6
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## v0.82.1
|
||||
|
||||
### Fixed
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Fixed representation of transforms when view is originally zero-sized ([a81e94a40c](https://github.com/facebook/react-native/commit/a81e94a40ca8dca9e57b562db21f8f235c5b25a0) by [@javache](https://github.com/javache))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Fixed issue when using gnu coreutils cp command when using precompiled binaries causing compilation error ([068ec39aea](https://github.com/facebook/react-native/commit/068ec39aea543617e5159fe22274b294bfb29026) by [@chrfalch](https://github.com/chrfalch))
|
||||
|
||||
## v0.82.0
|
||||
|
||||
### Breaking
|
||||
@@ -812,6 +824,10 @@
|
||||
- **Text:** Selection range not respected when changing text or selection when selection is forced ([d32ea66e6a](https://github.com/facebook/react-native/commit/d32ea66e6a945dd84092532401b265b12d482668) by Olivier Bouillet)
|
||||
- **TextInput:** Fix TextInput `onContentSizeChange` event being dispatched only once on iOS on the new architecture ([5fd5188172](https://github.com/facebook/react-native/commit/5fd51881727b2d86f87abf04db032940ac0ec8c4) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
|
||||
## v0.79.7
|
||||
|
||||
See [CHANGELOG-0.7x](./CHANGELOG-0.7x.md#v0797)
|
||||
|
||||
## v0.79.6
|
||||
|
||||
See [CHANGELOG-0.7x](./CHANGELOG-0.7x.md#v0796)
|
||||
|
||||
+1
-6
@@ -104,10 +104,6 @@ tasks.register("build") {
|
||||
tasks.register("publishAllToMavenTempLocal") {
|
||||
description = "Publish all the artifacts to be available inside a Maven Local repository on /tmp."
|
||||
dependsOn(":packages:react-native:ReactAndroid:publishAllPublicationsToMavenTempLocalRepository")
|
||||
// We don't publish the external-artifacts to Maven Local as ci is using it via workspace.
|
||||
dependsOn(
|
||||
":packages:react-native:ReactAndroid:hermes-engine:publishAllPublicationsToMavenTempLocalRepository"
|
||||
)
|
||||
}
|
||||
|
||||
tasks.register("publishAndroidToSonatype") {
|
||||
@@ -135,8 +131,7 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(project(":packages:react-native:ReactAndroid:hermes-engine"))
|
||||
// TODO: T237406039 update coordinates
|
||||
.using(module("com.facebook.react:hermes-android:0.+"))
|
||||
.using(module("com.facebook.hermes:hermes-android:0.+"))
|
||||
.because("Users opted to use hermes from nightly")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -86,7 +86,7 @@
|
||||
"eslint-plugin-relay": "^1.8.3",
|
||||
"fb-dotslash": "0.5.8",
|
||||
"flow-api-translator": "0.32.0",
|
||||
"flow-bin": "^0.288.0",
|
||||
"flow-bin": "^0.289.0",
|
||||
"glob": "^7.1.1",
|
||||
"hermes-eslint": "0.32.0",
|
||||
"hermes-transform": "0.32.0",
|
||||
@@ -120,7 +120,6 @@
|
||||
"ws": "^7.5.10"
|
||||
},
|
||||
"resolutions": {
|
||||
"eslint-plugin-react-hooks": "6.1.0-canary-12bc60f5-20250613",
|
||||
"react-is": "19.2.0",
|
||||
"on-headers": "1.1.0",
|
||||
"compression": "1.8.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@generated SignedSource<<ec85a720cb9603510d40915ba1410b50>>
|
||||
Git revision: 54afd8765db32a9640edef3788085b991fe4b88a
|
||||
@generated SignedSource<<4bb67766e15e25a481c4c38873260a7a>>
|
||||
Git revision: 7aa57d13e50ce9d74a91c9315c9b0ded00fbc19f
|
||||
Built with --nohooks: false
|
||||
Is local checkout: false
|
||||
Remote URL: https://github.com/facebook/react-native-devtools-frontend
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
import*as e from"../root/root.js";const t=e.Runtime.RNExperimentName,n={didInitializeExperiments:!1,isReactNativeEntryPoint:!1};class i{name;title;unstable;docLink;feedbackLink;enabledByDefault;constructor(e){this.name=e.name,this.title=e.title,this.unstable=e.unstable,this.docLink=e.docLink,this.feedbackLink=e.feedbackLink,this.enabledByDefault=function(e,t){if(null==e)return()=>t;if("boolean"==typeof e)return()=>e;return e}(e.enabledByDefault,!1)}}const r=new class{#e=new Map;#t=new Set;register(e){if(n.didInitializeExperiments)throw new Error("Experiments must be registered before constructing MainImpl");const{name:t}=e;if(this.#e.has(t))throw new Error(`React Native Experiment ${t} is already registered`);this.#e.set(t,new i(e))}enableExperimentsByDefault(e){if(n.didInitializeExperiments)throw new Error("Experiments must be configured before constructing MainImpl");for(const n of e)if(Object.prototype.hasOwnProperty.call(t,n)){const e=this.#e.get(n);if(!e)throw new Error(`React Native Experiment ${n} is not registered`);e.enabledByDefault=()=>!0}else this.#t.add(n)}copyInto(e,t=""){for(const[i,r]of this.#e)e.register(i,t+r.title,r.unstable,r.docLink,r.feedbackLink),r.enabledByDefault({isReactNativeEntryPoint:n.isReactNativeEntryPoint})&&e.enableExperimentsByDefault([i]);for(const t of this.#t)e.enableExperimentsByDefault([t]);n.didInitializeExperiments=!0}};r.register({name:t.JS_HEAP_PROFILER_ENABLE,title:"Enable Heap Profiler (Memory Panel)",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>!e}),r.register({name:t.REACT_NATIVE_SPECIFIC_UI,title:"Show React Native-specific UI",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>e}),r.register({name:t.ENABLE_PERFORMANCE_PANEL,title:"Enable Performance panel",unstable:!0,enabledByDefault:({isReactNativeEntryPoint:e})=>!e}),r.register({name:t.ENABLE_NETWORK_PANEL,title:"Enable Network panel",unstable:!0,enabledByDefault:()=>!1});var a=Object.freeze({__proto__:null,Instance:r,RNExperimentName:t,setIsReactNativeEntryPoint:function(e){if(n.didInitializeExperiments)throw new Error("setIsReactNativeEntryPoint must be called before constructing MainImpl");n.isReactNativeEntryPoint=e}});export{a as RNExperimentsImpl};
|
||||
import*as e from"../root/root.js";const t=e.Runtime.RNExperimentName,i={didInitializeExperiments:!1,isReactNativeEntryPoint:!1};class n{name;title;unstable;docLink;feedbackLink;enabledByDefault;constructor(e){this.name=e.name,this.title=e.title,this.unstable=e.unstable,this.docLink=e.docLink,this.feedbackLink=e.feedbackLink,this.enabledByDefault=function(e,t){if(null==e)return()=>t;if("boolean"==typeof e)return()=>e;return e}(e.enabledByDefault,!1)}}const r=new class{#e=new Map;#t=new Set;register(e){if(i.didInitializeExperiments)throw new Error("Experiments must be registered before constructing MainImpl");const{name:t}=e;if(this.#e.has(t))throw new Error(`React Native Experiment ${t} is already registered`);this.#e.set(t,new n(e))}enableExperimentsByDefault(e){if(i.didInitializeExperiments)throw new Error("Experiments must be configured before constructing MainImpl");for(const i of e)if(Object.prototype.hasOwnProperty.call(t,i)){const e=this.#e.get(i);if(!e)throw new Error(`React Native Experiment ${i} is not registered`);e.enabledByDefault=()=>!0}else this.#t.add(i)}copyInto(e,t=""){for(const[n,r]of this.#e)e.register(n,t+r.title,r.unstable,r.docLink,r.feedbackLink),r.enabledByDefault({isReactNativeEntryPoint:i.isReactNativeEntryPoint})&&e.enableExperimentsByDefault([n]);for(const t of this.#t)e.enableExperimentsByDefault([t]);i.didInitializeExperiments=!0}};r.register({name:t.JS_HEAP_PROFILER_ENABLE,title:"Enable Heap Profiler (Memory Panel)",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>!e}),r.register({name:t.REACT_NATIVE_SPECIFIC_UI,title:"Show React Native-specific UI",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>e}),r.register({name:t.ENABLE_NETWORK_PANEL,title:"Enable Network panel",unstable:!0,enabledByDefault:()=>!1});var a=Object.freeze({__proto__:null,Instance:r,RNExperimentName:t,setIsReactNativeEntryPoint:function(e){if(i.didInitializeExperiments)throw new Error("setIsReactNativeEntryPoint must be called before constructing MainImpl");i.isReactNativeEntryPoint=e}});export{a as RNExperimentsImpl};
|
||||
|
||||
@@ -1 +1 @@
|
||||
import*as e from"../platform/platform.js";const t=new URLSearchParams(location.search);let n,r,s="";function a(){return window.location.pathname}function i(e){return["node_app","js_app"].some((t=>e.includes(t)))}class o{constructor(){}static instance(e={forceNew:null}){const{forceNew:t}=e;return n&&!t||(n=new o),n}static removeInstance(){n=void 0}static queryParam(e){return t.get(e)}static setQueryParamForTesting(e,n){t.set(e,n)}static isNode(){return void 0===r&&(r=i(a())),r}static setPlatform(e){s=e}static platform(){return s}static isDescriptorEnabled(e){const{experiment:t}=e;if("*"===t)return!0;if(t&&t.startsWith("!")&&h.isEnabled(t.substring(1)))return!1;if(t&&!t.startsWith("!")&&!h.isEnabled(t))return!1;const{condition:n}=e;return!n||n(_)}loadLegacyModule(e){console.log("Loading legacy module: "+e);return import(`../../${e}`).then((t=>(console.log("Loaded legacy module: "+e),t)))}}class l{#e=[];#t=new Set;#n=new Set;#r=new Set;#s=new Set;#a=new c;allConfigurableExperiments(){const e=[];for(const t of this.#e)this.#n.has(t.name)||e.push(t);return e}register(t,n,r,s,a){if(this.#t.has(t))throw new Error(`Duplicate registration of experiment '${t}'`);this.#t.add(t),this.#e.push(new m(this,t,n,Boolean(r),s??e.DevToolsPath.EmptyUrlString,a??e.DevToolsPath.EmptyUrlString))}isEnabled(e){return this.checkExperiment(e),!1!==this.#a.get(e)&&(!(!this.#n.has(e)&&!this.#r.has(e))||(!!this.#s.has(e)||Boolean(this.#a.get(e))))}setEnabled(e,t){this.checkExperiment(e),this.#a.set(e,t)}enableExperimentsTransiently(e){for(const t of e)this.checkExperiment(t),this.#n.add(t)}enableExperimentsByDefault(e){for(const t of e)this.checkExperiment(t),this.#r.add(t)}setServerEnabledExperiments(e){for(const t of e)this.checkExperiment(t),this.#s.add(t)}enableForTest(e){this.checkExperiment(e),this.#n.add(e)}disableForTest(e){this.checkExperiment(e),this.#n.delete(e)}clearForTest(){this.#e=[],this.#t.clear(),this.#n.clear(),this.#r.clear(),this.#s.clear()}cleanUpStaleExperiments(){this.#a.cleanUpStaleExperiments(this.#t)}checkExperiment(e){if(!this.#t.has(e))throw new Error(`Unknown experiment '${e}'`)}}class c{#e={};constructor(){try{const e=self.localStorage?.getItem("experiments");e&&(this.#e=JSON.parse(e))}catch{console.error("Failed to parse localStorage['experiments']")}}get(e){return this.#e[e]}set(e,t){this.#e[e]=t,this.#i()}cleanUpStaleExperiments(e){for(const[t]of Object.entries(this.#e))e.has(t)||delete this.#e[t];this.#i()}#i(){self.localStorage?.setItem("experiments",JSON.stringify(this.#e))}}class m{name;title;unstable;docLink;feedbackLink;#e;constructor(e,t,n,r,s,a){this.name=t,this.title=n,this.unstable=r,this.docLink=s,this.feedbackLink=a,this.#e=e}isEnabled(){return this.#e.isEnabled(this.name)}setEnabled(e){this.#e.setEnabled(this.name,e)}}const h=new l;var E,p,d,u;!function(e){e.REACT_NATIVE_SPECIFIC_UI="react-native-specific-ui",e.JS_HEAP_PROFILER_ENABLE="js-heap-profiler-enable",e.ENABLE_PERFORMANCE_PANEL="enable-performance-panel",e.ENABLE_NETWORK_PANEL="enable-network-panel"}(E||(E={})),function(e){e.NOT_SOURCES_HIDE_ADD_FOLDER="!sources.hide_add_folder",e.REACT_NATIVE_UNSTABLE_NETWORK_PANEL="unstable_enableNetworkPanel"}(p||(p={})),function(e){e[e.ALLOW=0]="ALLOW",e[e.ALLOW_WITHOUT_LOGGING=1]="ALLOW_WITHOUT_LOGGING",e[e.DISABLE=2]="DISABLE"}(d||(d={})),function(e){e.ALL_SCRIPTS="ALL_SCRIPTS",e.SIDE_EFFECT_FREE_SCRIPTS_ONLY="SIDE_EFFECT_FREE_SCRIPTS_ONLY",e.NO_SCRIPTS="NO_SCRIPTS"}(u||(u={}));const _=Object.create(null),f={canDock:()=>Boolean(o.queryParam("can_dock")),notSourcesHideAddFolder:()=>Boolean(o.queryParam(p.NOT_SOURCES_HIDE_ADD_FOLDER)),reactNativeUnstableNetworkPanel:()=>Boolean(o.queryParam(p.REACT_NATIVE_UNSTABLE_NETWORK_PANEL))||h.isEnabled("enable-network-panel")};var x=Object.freeze({__proto__:null,get ConditionName(){return p},Experiment:m,ExperimentsSupport:l,get GenAiEnterprisePolicyValue(){return d},get HostConfigFreestylerExecutionMode(){return u},get RNExperimentName(){return E},Runtime:o,conditions:f,experiments:h,getChromeVersion:()=>{const e=navigator.userAgent.match(/(?:^|\W)(?:Chrome|HeadlessChrome)\/(\S+)/);return e&&e.length>1?e[1]:""},getPathName:a,getRemoteBase:function(e=self.location.toString()){const t=new URL(e).searchParams.get("remoteBase");if(!t)return null;const n=/\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(t);return n?{base:`devtools://devtools/remote/serve_file/${n[1]}/`,version:n[1]}:null},hostConfig:_,isNodeEntry:i});export{x as Runtime};
|
||||
import*as e from"../platform/platform.js";const t=new URLSearchParams(location.search);let n,r,s="";function a(){return window.location.pathname}function i(e){return["node_app","js_app"].some((t=>e.includes(t)))}class o{constructor(){}static instance(e={forceNew:null}){const{forceNew:t}=e;return n&&!t||(n=new o),n}static removeInstance(){n=void 0}static queryParam(e){return t.get(e)}static setQueryParamForTesting(e,n){t.set(e,n)}static isNode(){return void 0===r&&(r=i(a())),r}static setPlatform(e){s=e}static platform(){return s}static isDescriptorEnabled(e){const{experiment:t}=e;if("*"===t)return!0;if(t&&t.startsWith("!")&&h.isEnabled(t.substring(1)))return!1;if(t&&!t.startsWith("!")&&!h.isEnabled(t))return!1;const{condition:n}=e;return!n||n(_)}loadLegacyModule(e){console.log("Loading legacy module: "+e);return import(`../../${e}`).then((t=>(console.log("Loaded legacy module: "+e),t)))}}class l{#e=[];#t=new Set;#n=new Set;#r=new Set;#s=new Set;#a=new c;allConfigurableExperiments(){const e=[];for(const t of this.#e)this.#n.has(t.name)||e.push(t);return e}register(t,n,r,s,a){if(this.#t.has(t))throw new Error(`Duplicate registration of experiment '${t}'`);this.#t.add(t),this.#e.push(new m(this,t,n,Boolean(r),s??e.DevToolsPath.EmptyUrlString,a??e.DevToolsPath.EmptyUrlString))}isEnabled(e){return this.checkExperiment(e),!1!==this.#a.get(e)&&(!(!this.#n.has(e)&&!this.#r.has(e))||(!!this.#s.has(e)||Boolean(this.#a.get(e))))}setEnabled(e,t){this.checkExperiment(e),this.#a.set(e,t)}enableExperimentsTransiently(e){for(const t of e)this.checkExperiment(t),this.#n.add(t)}enableExperimentsByDefault(e){for(const t of e)this.checkExperiment(t),this.#r.add(t)}setServerEnabledExperiments(e){for(const t of e)this.checkExperiment(t),this.#s.add(t)}enableForTest(e){this.checkExperiment(e),this.#n.add(e)}disableForTest(e){this.checkExperiment(e),this.#n.delete(e)}clearForTest(){this.#e=[],this.#t.clear(),this.#n.clear(),this.#r.clear(),this.#s.clear()}cleanUpStaleExperiments(){this.#a.cleanUpStaleExperiments(this.#t)}checkExperiment(e){if(!this.#t.has(e))throw new Error(`Unknown experiment '${e}'`)}}class c{#e={};constructor(){try{const e=self.localStorage?.getItem("experiments");e&&(this.#e=JSON.parse(e))}catch{console.error("Failed to parse localStorage['experiments']")}}get(e){return this.#e[e]}set(e,t){this.#e[e]=t,this.#i()}cleanUpStaleExperiments(e){for(const[t]of Object.entries(this.#e))e.has(t)||delete this.#e[t];this.#i()}#i(){self.localStorage?.setItem("experiments",JSON.stringify(this.#e))}}class m{name;title;unstable;docLink;feedbackLink;#e;constructor(e,t,n,r,s,a){this.name=t,this.title=n,this.unstable=r,this.docLink=s,this.feedbackLink=a,this.#e=e}isEnabled(){return this.#e.isEnabled(this.name)}setEnabled(e){this.#e.setEnabled(this.name,e)}}const h=new l;var E,p,d,u;!function(e){e.REACT_NATIVE_SPECIFIC_UI="react-native-specific-ui",e.JS_HEAP_PROFILER_ENABLE="js-heap-profiler-enable",e.ENABLE_NETWORK_PANEL="enable-network-panel"}(E||(E={})),function(e){e.NOT_SOURCES_HIDE_ADD_FOLDER="!sources.hide_add_folder",e.REACT_NATIVE_UNSTABLE_NETWORK_PANEL="unstable_enableNetworkPanel"}(p||(p={})),function(e){e[e.ALLOW=0]="ALLOW",e[e.ALLOW_WITHOUT_LOGGING=1]="ALLOW_WITHOUT_LOGGING",e[e.DISABLE=2]="DISABLE"}(d||(d={})),function(e){e.ALL_SCRIPTS="ALL_SCRIPTS",e.SIDE_EFFECT_FREE_SCRIPTS_ONLY="SIDE_EFFECT_FREE_SCRIPTS_ONLY",e.NO_SCRIPTS="NO_SCRIPTS"}(u||(u={}));const _=Object.create(null),f={canDock:()=>Boolean(o.queryParam("can_dock")),notSourcesHideAddFolder:()=>Boolean(o.queryParam(p.NOT_SOURCES_HIDE_ADD_FOLDER)),reactNativeUnstableNetworkPanel:()=>Boolean(o.queryParam(p.REACT_NATIVE_UNSTABLE_NETWORK_PANEL))||h.isEnabled("enable-network-panel")};var x=Object.freeze({__proto__:null,get ConditionName(){return p},Experiment:m,ExperimentsSupport:l,get GenAiEnterprisePolicyValue(){return d},get HostConfigFreestylerExecutionMode(){return u},get RNExperimentName(){return E},Runtime:o,conditions:f,experiments:h,getChromeVersion:()=>{const e=navigator.userAgent.match(/(?:^|\W)(?:Chrome|HeadlessChrome)\/(\S+)/);return e&&e.length>1?e[1]:""},getPathName:a,getRemoteBase:function(e=self.location.toString()){const t=new URL(e).searchParams.get("remoteBase");if(!t)return null;const n=/\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(t);return n?{base:`devtools://devtools/remote/serve_file/${n[1]}/`,version:n[1]}:null},hostConfig:_,isNodeEntry:i});export{x as Runtime};
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -30,7 +30,7 @@
|
||||
"eslint-plugin-ft-flow": "^2.0.1",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-native": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
-3
@@ -14,7 +14,6 @@ import com.facebook.react.utils.KotlinStdlibCompatUtils.capitalizeCompat
|
||||
import com.facebook.react.utils.NdkConfiguratorUtils.configureJsEnginePackagingOptions
|
||||
import com.facebook.react.utils.NdkConfiguratorUtils.configureNewArchPackagingOptions
|
||||
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
|
||||
import com.facebook.react.utils.ProjectUtils.useThirdPartyJSC
|
||||
import com.facebook.react.utils.detectedCliFile
|
||||
import com.facebook.react.utils.detectedEntryFile
|
||||
@@ -49,7 +48,6 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
|
||||
} else {
|
||||
isHermesEnabledInProject
|
||||
}
|
||||
val isHermesV1Enabled = project.isHermesV1Enabled || rootProject.isHermesV1Enabled
|
||||
val isDebuggableVariant =
|
||||
config.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
|
||||
val useThirdPartyJSC = project.useThirdPartyJSC
|
||||
@@ -80,7 +78,6 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
|
||||
task.jsBundleDir.set(jsBundleDir)
|
||||
task.resourcesDir.set(resourcesDir)
|
||||
task.hermesEnabled.set(isHermesEnabledInThisVariant)
|
||||
task.hermesV1Enabled.set(isHermesV1Enabled)
|
||||
task.minifyEnabled.set(!isHermesEnabledInThisVariant)
|
||||
task.devEnabled.set(false)
|
||||
task.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
|
||||
|
||||
+1
-4
@@ -63,8 +63,6 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
|
||||
@get:Input abstract val hermesEnabled: Property<Boolean>
|
||||
|
||||
@get:Input abstract val hermesV1Enabled: Property<Boolean>
|
||||
|
||||
@get:Input abstract val devEnabled: Property<Boolean>
|
||||
|
||||
@get:Input abstract val extraPackagerArgs: ListProperty<String>
|
||||
@@ -96,8 +94,7 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
runCommand(bundleCommand)
|
||||
|
||||
if (hermesEnabled.get()) {
|
||||
val detectedHermesCommand =
|
||||
detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled.get())
|
||||
val detectedHermesCommand = detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get())
|
||||
val bytecodeFile = File("${bundleFile}.hbc")
|
||||
val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
|
||||
val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
|
||||
|
||||
+26
-17
@@ -14,6 +14,7 @@ import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY
|
||||
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_PUBLISHING_GROUP
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_V1_VERSION_NAME
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_VERSION_NAME
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_PUBLISHING_GROUP
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY
|
||||
@@ -139,11 +140,7 @@ internal object DependencyUtils {
|
||||
// Contributors only: The hermes-engine version is forced only if the user has
|
||||
// not opted into using nightlies for local development.
|
||||
configuration.resolutionStrategy.force(
|
||||
// TODO: T237406039 update coordinates
|
||||
if (hermesV1Enabled)
|
||||
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
|
||||
else
|
||||
"${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
|
||||
"${coordinates.hermesGroupString}:hermes-android:${if (hermesV1Enabled) coordinates.hermesV1VersionString else coordinates.hermesVersionString}"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -154,13 +151,10 @@ internal object DependencyUtils {
|
||||
coordinates: Coordinates,
|
||||
hermesV1Enabled: Boolean = false,
|
||||
): List<Triple<String, String, String>> {
|
||||
// TODO: T231755027 update coordinates and versioning
|
||||
val dependencySubstitution = mutableListOf<Triple<String, String, String>>()
|
||||
// TODO: T237406039 update coordinates
|
||||
val hermesVersionString =
|
||||
if (hermesV1Enabled)
|
||||
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
|
||||
else "${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
|
||||
val hermesVersion =
|
||||
if (hermesV1Enabled) coordinates.hermesV1VersionString else coordinates.hermesVersionString
|
||||
val hermesVersionString = "${coordinates.hermesGroupString}:hermes-android:${hermesVersion}"
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:react-native",
|
||||
@@ -175,6 +169,13 @@ internal object DependencyUtils {
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.",
|
||||
)
|
||||
)
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:hermes-android",
|
||||
hermesVersionString,
|
||||
"The hermes-android artifact was moved to com.facebook.hermes publishing group.",
|
||||
)
|
||||
)
|
||||
if (coordinates.reactGroupString != DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP) {
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
@@ -183,7 +184,6 @@ internal object DependencyUtils {
|
||||
"The react-android dependency was modified to use the correct Maven group.",
|
||||
)
|
||||
)
|
||||
// TODO: T237406039 update coordinates
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:hermes-android",
|
||||
@@ -191,12 +191,13 @@ internal object DependencyUtils {
|
||||
"The hermes-android dependency was modified to use the correct Maven group.",
|
||||
)
|
||||
)
|
||||
} else if (hermesV1Enabled) {
|
||||
}
|
||||
if (coordinates.hermesGroupString != DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP) {
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:hermes-android",
|
||||
"com.facebook.hermes:hermes-android",
|
||||
hermesVersionString,
|
||||
"The hermes-android dependency was modified to use Hermes V1.",
|
||||
"The hermes-android dependency was modified to use the correct Maven group.",
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -221,11 +222,19 @@ internal object DependencyUtils {
|
||||
val hermesGroupString =
|
||||
reactAndroidProperties[INTERNAL_HERMES_PUBLISHING_GROUP] as? String
|
||||
?: DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
|
||||
// TODO: T237406039 read both versions from the same file
|
||||
|
||||
val hermesVersionProperties = Properties()
|
||||
hermesVersionFile.inputStream().use { hermesVersionProperties.load(it) }
|
||||
|
||||
val hermesVersion = versionString
|
||||
val hermesVersionString =
|
||||
(hermesVersionProperties[INTERNAL_HERMES_VERSION_NAME] as? String).orEmpty()
|
||||
val hermesVersion =
|
||||
if (hermesVersionString.startsWith("0.0.0") || "-commitly-" in hermesVersionString) {
|
||||
"$hermesVersionString-SNAPSHOT"
|
||||
} else {
|
||||
hermesVersionString
|
||||
}
|
||||
|
||||
val hermesV1Version =
|
||||
(hermesVersionProperties[INTERNAL_HERMES_V1_VERSION_NAME] as? String).orEmpty()
|
||||
|
||||
|
||||
+2
-8
@@ -130,7 +130,6 @@ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): F
|
||||
internal fun detectOSAwareHermesCommand(
|
||||
projectRoot: File,
|
||||
hermesCommand: String,
|
||||
hermesV1Enabled: Boolean = false,
|
||||
): String { // 1. If the project specifies a Hermes command, don't second guess it.
|
||||
if (hermesCommand.isNotBlank()) {
|
||||
val osSpecificHermesCommand =
|
||||
@@ -151,13 +150,9 @@ internal fun detectOSAwareHermesCommand(
|
||||
return builtHermesc.cliPath(projectRoot)
|
||||
}
|
||||
|
||||
// 3. If Hermes V1 is enabled, use hermes-compiler from npm, otherwise, if the
|
||||
// react-native contains a pre-built hermesc, use it.
|
||||
// TODO: T237406039 use hermes-compiler from npm for both
|
||||
val hermesCPath = if (hermesV1Enabled) HERMES_COMPILER_NPM_DIR else HERMESC_IN_REACT_NATIVE_DIR
|
||||
// 3. Use hermes-compiler from npm
|
||||
val prebuiltHermesPath =
|
||||
hermesCPath
|
||||
.plus(getHermesCBin())
|
||||
HERMES_COMPILER_NPM_DIR.plus(getHermesCBin())
|
||||
.replace("%OS-BIN%", getHermesOSBin())
|
||||
// Execution on Windows fails with / as separator
|
||||
.replace('/', File.separatorChar)
|
||||
@@ -243,6 +238,5 @@ internal fun readPackageJsonFile(
|
||||
}
|
||||
|
||||
private const val HERMES_COMPILER_NPM_DIR = "node_modules/hermes-compiler/hermesc/%OS-BIN%/"
|
||||
private const val HERMESC_IN_REACT_NATIVE_DIR = "node_modules/react-native/sdks/hermesc/%OS-BIN%/"
|
||||
private const val HERMESC_BUILT_FROM_SOURCE_DIR =
|
||||
"node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/"
|
||||
|
||||
+3
-2
@@ -81,8 +81,9 @@ object PropertyUtils {
|
||||
const val INTERNAL_VERSION_NAME = "VERSION_NAME"
|
||||
|
||||
/**
|
||||
* Internal property, shared with iOS, used to control the version name of Hermes Engine. This is
|
||||
* stored in sdks/hermes-engine/version.properties
|
||||
* Internal properties, shared with iOS, used to control the version name of Hermes Engine. They
|
||||
* are stored in sdks/hermes-engine/version.properties
|
||||
*/
|
||||
const val INTERNAL_HERMES_VERSION_NAME = "HERMES_VERSION_NAME"
|
||||
const val INTERNAL_HERMES_V1_VERSION_NAME = "HERMES_V1_VERSION_NAME"
|
||||
}
|
||||
|
||||
+42
-21
@@ -289,7 +289,7 @@ class DependencyUtilsTest {
|
||||
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@@ -324,11 +324,11 @@ class DependencyUtilsTest {
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@@ -381,11 +381,15 @@ class DependencyUtilsTest {
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
|
||||
assertThat(
|
||||
appForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:4.5.6" }
|
||||
)
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
|
||||
assertThat(
|
||||
libForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:4.5.6" }
|
||||
)
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@@ -438,7 +442,7 @@ class DependencyUtilsTest {
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[0].third)
|
||||
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
|
||||
assertThat("com.facebook.react:hermes-android:0.42.0")
|
||||
assertThat("com.facebook.hermes:hermes-android:0.42.0")
|
||||
.isEqualTo(dependencySubstitutions[1].second)
|
||||
assertThat(
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
@@ -490,19 +494,26 @@ class DependencyUtilsTest {
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[0].third)
|
||||
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
|
||||
assertThat("io.github.test:hermes-android:0.42.0").isEqualTo(dependencySubstitutions[1].second)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.42.0")
|
||||
.isEqualTo(dependencySubstitutions[1].second)
|
||||
assertThat(
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[1].third)
|
||||
assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[2].first)
|
||||
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[2].second)
|
||||
assertThat("The react-android dependency was modified to use the correct Maven group.")
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[2].first)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.42.0")
|
||||
.isEqualTo(dependencySubstitutions[2].second)
|
||||
assertThat("The hermes-android artifact was moved to com.facebook.hermes publishing group.")
|
||||
.isEqualTo(dependencySubstitutions[2].third)
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[3].first)
|
||||
assertThat("io.github.test:hermes-android:0.42.0").isEqualTo(dependencySubstitutions[3].second)
|
||||
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
|
||||
assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[3].first)
|
||||
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[3].second)
|
||||
assertThat("The react-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[3].third)
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[4].first)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.42.0")
|
||||
.isEqualTo(dependencySubstitutions[4].second)
|
||||
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[4].third)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -532,15 +543,20 @@ class DependencyUtilsTest {
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[1].third)
|
||||
assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[2].first)
|
||||
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[2].second)
|
||||
assertThat("The react-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[2].third)
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[3].first)
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[2].first)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.43.0")
|
||||
.isEqualTo(dependencySubstitutions[3].second)
|
||||
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[2].second)
|
||||
assertThat("The hermes-android artifact was moved to com.facebook.hermes publishing group.")
|
||||
.isEqualTo(dependencySubstitutions[2].third)
|
||||
assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[3].first)
|
||||
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[3].second)
|
||||
assertThat("The react-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[3].third)
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[4].first)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.43.0")
|
||||
.isEqualTo(dependencySubstitutions[4].second)
|
||||
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[4].third)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -560,6 +576,7 @@ class DependencyUtilsTest {
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_VERSION_NAME=1000.0.0
|
||||
HERMES_V1_VERSION_NAME=1000.0.0
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
@@ -596,6 +613,7 @@ class DependencyUtilsTest {
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_VERSION_NAME=0.14.0
|
||||
HERMES_V1_VERSION_NAME=250829098.0.0-stable
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
@@ -609,7 +627,7 @@ class DependencyUtilsTest {
|
||||
val hermesV1VersionString = strings.hermesV1VersionString
|
||||
|
||||
assertThat(versionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
|
||||
assertThat(hermesVersionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
|
||||
assertThat(hermesVersionString).isEqualTo("0.14.0")
|
||||
assertThat(hermesV1VersionString).isEqualTo("250829098.0.0-stable")
|
||||
}
|
||||
|
||||
@@ -661,6 +679,7 @@ class DependencyUtilsTest {
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_VERSION_NAME=
|
||||
HERMES_V1_VERSION_NAME=
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
@@ -695,6 +714,7 @@ class DependencyUtilsTest {
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_VERSION_NAME=
|
||||
HERMES_V1_VERSION_NAME=
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
@@ -726,6 +746,7 @@ class DependencyUtilsTest {
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_VERSION_NAME=
|
||||
HERMES_V1_VERSION_NAME=
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
|
||||
+2
-12
@@ -155,21 +155,11 @@ class PathUtilsTest {
|
||||
|
||||
@Test
|
||||
@WithOs(OS.MAC)
|
||||
fun detectOSAwareHermesCommand_withBundledHermescInsideRN() {
|
||||
tempFolder.newFolder("node_modules/react-native/sdks/hermesc/osx-bin/")
|
||||
val expected = tempFolder.newFile("node_modules/react-native/sdks/hermesc/osx-bin/hermesc")
|
||||
|
||||
assertThat(detectOSAwareHermesCommand(tempFolder.root, "")).isEqualTo(expected.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithOs(OS.MAC)
|
||||
fun detectOSAwareHermesCommand_withHermesV1Enabled() {
|
||||
fun detectOSAwareHermesCommand_withHermescFromNPM() {
|
||||
tempFolder.newFolder("node_modules/hermes-compiler/hermesc/osx-bin/")
|
||||
val expected = tempFolder.newFile("node_modules/hermes-compiler/hermesc/osx-bin/hermesc")
|
||||
|
||||
assertThat(detectOSAwareHermesCommand(tempFolder.root, "", hermesV1Enabled = true))
|
||||
.isEqualTo(expected.toString())
|
||||
assertThat(detectOSAwareHermesCommand(tempFolder.root, "")).isEqualTo(expected.toString())
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
|
||||
@@ -123,7 +123,8 @@ function getHermesLabel(): React.Node {
|
||||
|
||||
return (
|
||||
<ThemedText color="secondary" style={styles.label}>
|
||||
JS Engine: Hermes
|
||||
JS Engine: Hermes (
|
||||
{global.HermesInternal.getRuntimeProperties?.()['OSS Release Version']})
|
||||
</ThemedText>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ describe('console.timeStamp()', () => {
|
||||
|
||||
it("doesn't throw when additional arguments are specified", () => {
|
||||
expect(() =>
|
||||
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error'),
|
||||
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error', {
|
||||
tooltipText: 'Image processing failed',
|
||||
}),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
|
||||
@@ -24,11 +24,9 @@ RCT_EXPORT_MODULE(FileReaderModule)
|
||||
|
||||
@synthesize moduleRegistry = _moduleRegistry;
|
||||
|
||||
RCT_EXPORT_METHOD(readAsText
|
||||
: (NSDictionary<NSString *, id> *)blob encoding
|
||||
: (NSString *)encoding resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
readAsText : (NSDictionary<NSString *, id> *)blob encoding : (NSString *)encoding resolve : (RCTPromiseResolveBlock)
|
||||
resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"];
|
||||
dispatch_async(blobManager.methodQueue, ^{
|
||||
@@ -56,10 +54,9 @@ RCT_EXPORT_METHOD(readAsText
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(readAsDataURL
|
||||
: (NSDictionary<NSString *, id> *)blob resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
readAsDataURL : (NSDictionary<NSString *, id> *)blob resolve : (RCTPromiseResolveBlock)
|
||||
resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"];
|
||||
dispatch_async(blobManager.methodQueue, ^{
|
||||
|
||||
+43
-15
@@ -99,7 +99,7 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('NativeAccessibilityManagerIOS is not available'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -119,7 +119,11 @@ const AccessibilityInfo = {
|
||||
if (NativeAccessibilityInfoAndroid?.isGrayscaleEnabled != null) {
|
||||
NativeAccessibilityInfoAndroid.isGrayscaleEnabled(resolve);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'NativeAccessibilityInfoAndroid.isGrayscaleEnabled is not available',
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -130,7 +134,7 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('AccessibilityInfo native module is not available'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -150,7 +154,11 @@ const AccessibilityInfo = {
|
||||
if (NativeAccessibilityInfoAndroid?.isInvertColorsEnabled != null) {
|
||||
NativeAccessibilityInfoAndroid.isInvertColorsEnabled(resolve);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'NativeAccessibilityInfoAndroid.isInvertColorsEnabled is not available',
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -161,7 +169,7 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('AccessibilityInfo native module is not available'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -181,7 +189,7 @@ const AccessibilityInfo = {
|
||||
if (NativeAccessibilityInfoAndroid != null) {
|
||||
NativeAccessibilityInfoAndroid.isReduceMotionEnabled(resolve);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('AccessibilityInfo native module is not available'));
|
||||
}
|
||||
} else {
|
||||
if (NativeAccessibilityManagerIOS != null) {
|
||||
@@ -190,7 +198,7 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('NativeAccessibilityManagerIOS is not available'));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -208,7 +216,11 @@ const AccessibilityInfo = {
|
||||
if (NativeAccessibilityInfoAndroid?.isHighTextContrastEnabled != null) {
|
||||
NativeAccessibilityInfoAndroid.isHighTextContrastEnabled(resolve);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'NativeAccessibilityInfoAndroid.isHighTextContrastEnabled is not available',
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve(false);
|
||||
@@ -236,7 +248,11 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'NativeAccessibilityManagerIOS.getCurrentDarkerSystemColorsState is not available',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -264,7 +280,11 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'NativeAccessibilityManagerIOS.getCurrentPrefersCrossFadeTransitionsState is not available',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -289,7 +309,7 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('NativeAccessibilityManagerIOS is not available'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -309,7 +329,7 @@ const AccessibilityInfo = {
|
||||
if (NativeAccessibilityInfoAndroid != null) {
|
||||
NativeAccessibilityInfoAndroid.isTouchExplorationEnabled(resolve);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('NativeAccessibilityInfoAndroid is not available'));
|
||||
}
|
||||
} else {
|
||||
if (NativeAccessibilityManagerIOS != null) {
|
||||
@@ -318,7 +338,7 @@ const AccessibilityInfo = {
|
||||
reject,
|
||||
);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(new Error('NativeAccessibilityManagerIOS is not available'));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -343,10 +363,18 @@ const AccessibilityInfo = {
|
||||
) {
|
||||
NativeAccessibilityInfoAndroid.isAccessibilityServiceEnabled(resolve);
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'NativeAccessibilityInfoAndroid.isAccessibilityServiceEnabled is not available',
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
reject(null);
|
||||
reject(
|
||||
new Error(
|
||||
'isAccessibilityServiceEnabled is only available on Android',
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
+3
-9
@@ -12,12 +12,10 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import type {HostInstance} from 'react-native';
|
||||
|
||||
import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance';
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import * as React from 'react';
|
||||
import {createRef} from 'react';
|
||||
import {ScrollView} from 'react-native';
|
||||
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
|
||||
describe('onScroll', () => {
|
||||
it('delivers onScroll event', () => {
|
||||
@@ -36,11 +34,9 @@ describe('onScroll', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(scrollViewRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
scrollViewRef,
|
||||
'scroll',
|
||||
{
|
||||
contentOffset: {
|
||||
@@ -80,11 +76,9 @@ describe('onScroll', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(scrollViewRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
scrollViewRef,
|
||||
'scroll',
|
||||
{
|
||||
contentOffset: {
|
||||
@@ -97,7 +91,7 @@ describe('onScroll', () => {
|
||||
},
|
||||
);
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
scrollViewRef,
|
||||
'scroll',
|
||||
{
|
||||
contentOffset: {
|
||||
|
||||
Vendored
+28
-67
@@ -13,13 +13,11 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import type {HostInstance} from 'react-native';
|
||||
|
||||
import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance';
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import nullthrows from 'nullthrows';
|
||||
import * as React from 'react';
|
||||
import {createRef, useState} from 'react';
|
||||
import {FlatList, Modal, ScrollView, View} from 'react-native';
|
||||
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
|
||||
test('basic culling', () => {
|
||||
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
|
||||
@@ -46,9 +44,7 @@ test('basic culling', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
@@ -61,7 +57,7 @@ test('basic culling', () => {
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
@@ -118,10 +114,8 @@ test('recursive culling', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
// === Scroll down to the edge of child AA ===
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 30,
|
||||
});
|
||||
@@ -132,7 +126,7 @@ test('recursive culling', () => {
|
||||
|
||||
// === Scroll down past child AA ===
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 36,
|
||||
});
|
||||
@@ -144,7 +138,7 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// === Scroll down past child AB ===
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 51,
|
||||
});
|
||||
@@ -156,7 +150,7 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// === Scroll down past element A ===
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 56,
|
||||
});
|
||||
@@ -168,7 +162,7 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll element B into viewport. Just child BA should be created.
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 155,
|
||||
});
|
||||
@@ -182,7 +176,7 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll child BA into viewport.
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 165,
|
||||
});
|
||||
@@ -194,7 +188,7 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll back to start
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
@@ -216,7 +210,7 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll past element A
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 85,
|
||||
});
|
||||
@@ -265,9 +259,7 @@ test('recursive culling when initial offset is negative', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
@@ -328,9 +320,7 @@ test('deep nesting', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 40,
|
||||
});
|
||||
@@ -345,7 +335,7 @@ test('deep nesting', () => {
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 1, nativeID: "element B"}',
|
||||
]);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 150,
|
||||
});
|
||||
@@ -489,9 +479,7 @@ test('initial render', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 100,
|
||||
});
|
||||
@@ -561,9 +549,7 @@ test('basic culling smaller ScrollView', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 11,
|
||||
});
|
||||
@@ -624,9 +610,7 @@ test('culling with transform move', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
@@ -669,9 +653,7 @@ test('culling with recursive transform move', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
@@ -713,9 +695,7 @@ test('culling with transform scale', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 121,
|
||||
});
|
||||
@@ -774,10 +754,8 @@ test('culling inside of Modal', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueModalSizeUpdate(element, {
|
||||
Fantom.enqueueModalSizeUpdate(nodeRef, {
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
@@ -900,9 +878,7 @@ describe('reparenting', () => {
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
@@ -1578,10 +1554,8 @@ describe('reparenting', () => {
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
// Scroll down to see the grandchild.
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 115,
|
||||
});
|
||||
@@ -1669,10 +1643,8 @@ describe('reparenting', () => {
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
// Scroll down to see the grandchild.
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 118,
|
||||
});
|
||||
@@ -1911,10 +1883,8 @@ describe('reparenting', () => {
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
// Scroll to reveal grandchild.
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 70,
|
||||
});
|
||||
@@ -2001,10 +1971,8 @@ describe('reparenting', () => {
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
// Scroll to reveal grandchild.
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 70,
|
||||
});
|
||||
@@ -2214,9 +2182,7 @@ describe('reparenting', () => {
|
||||
'Insert {type: "View", parentNativeID: "unflattened", index: 0, nativeID: "child"}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 50,
|
||||
});
|
||||
@@ -2312,9 +2278,7 @@ describe('reparenting', () => {
|
||||
'Insert {type: "View", parentNativeID: "unflattened", index: 0, nativeID: "child"}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
Fantom.scrollTo(nodeRef, {
|
||||
x: 0,
|
||||
y: 50,
|
||||
});
|
||||
@@ -2463,10 +2427,9 @@ describe('opt out mechanism - Unstable_uncullableView & Unstable_uncullableTrace
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueModalSizeUpdate(element, {
|
||||
Fantom.enqueueModalSizeUpdate(nodeRef, {
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
@@ -2517,10 +2480,8 @@ describe('opt out mechanism - Unstable_uncullableView & Unstable_uncullableTrace
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueModalSizeUpdate(element, {
|
||||
Fantom.enqueueModalSizeUpdate(nodeRef, {
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
|
||||
+4
-13
@@ -12,7 +12,6 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import type {TextInputInstance} from '../TextInput.flow';
|
||||
|
||||
import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance';
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import nullthrows from 'nullthrows';
|
||||
import * as React from 'react';
|
||||
@@ -60,10 +59,8 @@ describe('<TextInput>', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(element, 'change', {
|
||||
Fantom.enqueueNativeEvent(nodeRef, 'change', {
|
||||
text: 'Hello World',
|
||||
});
|
||||
});
|
||||
@@ -86,10 +83,8 @@ describe('<TextInput>', () => {
|
||||
root.render(<TextInput onChangeText={onChangeText} ref={nodeRef} />);
|
||||
});
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(element, 'change', {
|
||||
Fantom.enqueueNativeEvent(nodeRef, 'change', {
|
||||
text: 'Hello World',
|
||||
});
|
||||
});
|
||||
@@ -113,12 +108,10 @@ describe('<TextInput>', () => {
|
||||
root.render(<TextInput onFocus={focusEvent} ref={nodeRef} />);
|
||||
});
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(element, 'focus');
|
||||
Fantom.enqueueNativeEvent(nodeRef, 'focus');
|
||||
});
|
||||
|
||||
// The tasks have not run.
|
||||
@@ -141,12 +134,10 @@ describe('<TextInput>', () => {
|
||||
root.render(<TextInput onBlur={blurEvent} ref={nodeRef} />);
|
||||
});
|
||||
|
||||
const element = ensureInstance(nodeRef.current, ReactNativeElement);
|
||||
|
||||
expect(blurEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(element, 'blur');
|
||||
Fantom.enqueueNativeEvent(nodeRef, 'blur');
|
||||
});
|
||||
|
||||
// The tasks have not run.
|
||||
|
||||
+21
-4
@@ -13,6 +13,9 @@ import type {AnyAttributeType} from '../../Renderer/shims/ReactNativeTypes';
|
||||
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
|
||||
import processAspectRatio from '../../StyleSheet/processAspectRatio';
|
||||
import processBackgroundImage from '../../StyleSheet/processBackgroundImage';
|
||||
import processBackgroundPosition from '../../StyleSheet/processBackgroundPosition';
|
||||
import processBackgroundRepeat from '../../StyleSheet/processBackgroundRepeat';
|
||||
import processBackgroundSize from '../../StyleSheet/processBackgroundSize';
|
||||
import processBoxShadow from '../../StyleSheet/processBoxShadow';
|
||||
import processColor from '../../StyleSheet/processColor';
|
||||
import processFilter from '../../StyleSheet/processFilter';
|
||||
@@ -144,11 +147,25 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
: {process: processBoxShadow},
|
||||
|
||||
/**
|
||||
* Linear Gradient
|
||||
* BackgroundImage
|
||||
*/
|
||||
experimental_backgroundImage: ReactNativeFeatureFlags.enableNativeCSSParsing()
|
||||
? true
|
||||
: {process: processBackgroundImage},
|
||||
experimental_backgroundImage: {process: processBackgroundImage},
|
||||
|
||||
/**
|
||||
* BackgroundSize
|
||||
*/
|
||||
experimental_backgroundSize: {process: processBackgroundSize},
|
||||
|
||||
/**
|
||||
* BackgroundPosition
|
||||
*/
|
||||
experimental_backgroundPosition: {process: processBackgroundPosition},
|
||||
|
||||
/**
|
||||
* BackgroundRepeat
|
||||
*/
|
||||
experimental_backgroundRepeat: {process: processBackgroundRepeat},
|
||||
|
||||
/**
|
||||
* View
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,9 @@ if (NativePerformance) {
|
||||
// $FlowExpectedError[cannot-write]
|
||||
global.performance = {
|
||||
mark: () => {},
|
||||
clearMarks: () => {},
|
||||
measure: () => {},
|
||||
clearMeasures: () => {},
|
||||
now: () => {
|
||||
const performanceNow = global.nativePerformanceNow || Date.now;
|
||||
return performanceNow();
|
||||
|
||||
@@ -332,7 +332,9 @@ class DebuggingOverlayRegistry {
|
||||
instance.measure((x, y, width, height, left, top) => {
|
||||
// measure can execute callback without any values provided to signal error.
|
||||
if (left == null || top == null || width == null || height == null) {
|
||||
reject('Unexpectedly failed to call measure on an instance.');
|
||||
reject(
|
||||
new Error('Unexpectedly failed to call measure on an instance.'),
|
||||
);
|
||||
}
|
||||
|
||||
resolve({
|
||||
@@ -480,7 +482,11 @@ class DebuggingOverlayRegistry {
|
||||
width == null ||
|
||||
height == null
|
||||
) {
|
||||
reject('Unexpectedly failed to call measure on an instance.');
|
||||
reject(
|
||||
new Error(
|
||||
'Unexpectedly failed to call measure on an instance.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
resolve({x: left, y: top, width, height});
|
||||
|
||||
@@ -30,8 +30,11 @@ class RCTDeviceEventEmitterImpl extends EventEmitter<RCTDeviceEventDefinitions>
|
||||
...args: RCTDeviceEventDefinitions[TEvent]
|
||||
): void {
|
||||
beginEvent(() => `RCTDeviceEventEmitter.emit#${eventType}`);
|
||||
super.emit(eventType, ...args);
|
||||
endEvent();
|
||||
try {
|
||||
super.emit(eventType, ...args);
|
||||
} finally {
|
||||
endEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
const RCTDeviceEventEmitter: IEventEmitter<RCTDeviceEventDefinitions> =
|
||||
|
||||
@@ -26,68 +26,81 @@ class LazyIterator {
|
||||
public:
|
||||
LazyIterator() = default;
|
||||
|
||||
LazyIterator(U vector, convert_type convert, size_type i)
|
||||
: _v(vector), _i(i), _convert(std::move(convert)) {}
|
||||
LazyIterator(U vector, convert_type convert, size_type i) : _v(vector), _i(i), _convert(std::move(convert)) {}
|
||||
|
||||
bool operator==(const LazyIterator& other) const {
|
||||
bool operator==(const LazyIterator &other) const
|
||||
{
|
||||
return _i == other._i && _v == other._v;
|
||||
}
|
||||
|
||||
bool operator<(const LazyIterator& b) const {
|
||||
bool operator<(const LazyIterator &b) const
|
||||
{
|
||||
return _i < b._i;
|
||||
}
|
||||
|
||||
value_type operator*() const {
|
||||
value_type operator*() const
|
||||
{
|
||||
return _convert(_v[_i]);
|
||||
}
|
||||
|
||||
std::unique_ptr<value_type> operator->() const {
|
||||
std::unique_ptr<value_type> operator->() const
|
||||
{
|
||||
return std::make_unique<value_type>(*this);
|
||||
}
|
||||
|
||||
LazyIterator operator+(difference_type n) const {
|
||||
LazyIterator operator+(difference_type n) const
|
||||
{
|
||||
return LazyIterator(_v, _convert, _i + n);
|
||||
}
|
||||
|
||||
LazyIterator& operator+=(difference_type n) {
|
||||
LazyIterator &operator+=(difference_type n)
|
||||
{
|
||||
_i += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LazyIterator& operator-=(difference_type n) {
|
||||
LazyIterator &operator-=(difference_type n)
|
||||
{
|
||||
_i -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LazyIterator operator-(difference_type n) const {
|
||||
LazyIterator operator-(difference_type n) const
|
||||
{
|
||||
return LazyIterator(_v, _i - n);
|
||||
}
|
||||
|
||||
difference_type operator-(const LazyIterator& a) const {
|
||||
difference_type operator-(const LazyIterator &a) const
|
||||
{
|
||||
return _i - a._i;
|
||||
}
|
||||
|
||||
LazyIterator& operator++() {
|
||||
LazyIterator &operator++()
|
||||
{
|
||||
return *this += 1;
|
||||
}
|
||||
|
||||
LazyIterator operator++(int) {
|
||||
LazyIterator operator++(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
LazyIterator& operator--() {
|
||||
LazyIterator &operator--()
|
||||
{
|
||||
return *this -= 1;
|
||||
}
|
||||
|
||||
LazyIterator operator--(int) {
|
||||
LazyIterator operator--(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
--*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
value_type operator[](difference_type n) const {
|
||||
value_type operator[](difference_type n) const
|
||||
{
|
||||
return _convert(_v[_i + n]);
|
||||
}
|
||||
|
||||
@@ -98,29 +111,32 @@ class LazyIterator {
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
LazyIterator<T, U> operator+(
|
||||
typename LazyIterator<T, U>::difference_type n,
|
||||
const LazyIterator<T, U>& i) {
|
||||
LazyIterator<T, U> operator+(typename LazyIterator<T, U>::difference_type n, const LazyIterator<T, U> &i)
|
||||
{
|
||||
return i + n;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const LazyIterator<T, U>& a, const LazyIterator<T, U>& b) {
|
||||
bool operator!=(const LazyIterator<T, U> &a, const LazyIterator<T, U> &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator<=(const LazyIterator<T, U>& a, const LazyIterator<T, U>& b) {
|
||||
bool operator<=(const LazyIterator<T, U> &a, const LazyIterator<T, U> &b)
|
||||
{
|
||||
return a < b || a == b;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator>(const LazyIterator<T, U>& a, const LazyIterator<T, U>& b) {
|
||||
bool operator>(const LazyIterator<T, U> &a, const LazyIterator<T, U> &b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator>=(const LazyIterator<T, U>& a, const LazyIterator<T, U>& b) {
|
||||
bool operator>=(const LazyIterator<T, U> &a, const LazyIterator<T, U> &b)
|
||||
{
|
||||
return a > b || a == b;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,24 +31,28 @@ class LazyVector {
|
||||
using size_type = std::int32_t;
|
||||
using convert_type = std::function<T(U)>;
|
||||
|
||||
static LazyVector<T, U>
|
||||
fromUnsafeRawValue(U v, size_type size, convert_type convert) {
|
||||
static LazyVector<T, U> fromUnsafeRawValue(U v, size_type size, convert_type convert)
|
||||
{
|
||||
return {v, size, convert};
|
||||
}
|
||||
|
||||
U unsafeRawValue() const {
|
||||
U unsafeRawValue() const
|
||||
{
|
||||
return _v;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
bool empty() const
|
||||
{
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
size_type size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
const_reference at(size_type pos) const {
|
||||
const_reference at(size_type pos) const
|
||||
{
|
||||
#ifndef _LIBCPP_NO_EXCEPTIONS
|
||||
if (!(pos < _size)) {
|
||||
throw std::out_of_range("out of range");
|
||||
@@ -59,41 +63,47 @@ class LazyVector {
|
||||
return _convert(_v[pos]);
|
||||
}
|
||||
|
||||
const_reference operator[](size_type pos) const {
|
||||
const_reference operator[](size_type pos) const
|
||||
{
|
||||
assert(pos < _size);
|
||||
return _convert(_v[pos]);
|
||||
}
|
||||
|
||||
const_reference front() const {
|
||||
const_reference front() const
|
||||
{
|
||||
assert(_size);
|
||||
return (*this)[0];
|
||||
}
|
||||
|
||||
const_reference back() const {
|
||||
const_reference back() const
|
||||
{
|
||||
assert(_size);
|
||||
return (*this)[_size - 1];
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(_v, _convert, 0);
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(_v, _convert, _size);
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
private:
|
||||
/** Wrapped vector */
|
||||
LazyVector(U vector, size_type size, convert_type convert)
|
||||
: _v(vector), _size(size), _convert(convert) {}
|
||||
LazyVector(U vector, size_type size, convert_type convert) : _v(vector), _size(size), _convert(convert) {}
|
||||
|
||||
U _v;
|
||||
size_type _size;
|
||||
|
||||
@@ -38,11 +38,9 @@ RCT_EXPORT_MODULE()
|
||||
* be scaled down to `displaySize` rather than `size`.
|
||||
* All units are in px (not points).
|
||||
*/
|
||||
RCT_EXPORT_METHOD(cropImage
|
||||
: (NSURLRequest *)imageRequest cropData
|
||||
: (JS::NativeImageEditor::Options &)cropData successCallback
|
||||
: (RCTResponseSenderBlock)successCallback errorCallback
|
||||
: (RCTResponseSenderBlock)errorCallback)
|
||||
RCT_EXPORT_METHOD(
|
||||
cropImage : (NSURLRequest *)imageRequest cropData : (JS::NativeImageEditor::Options &)cropData successCallback : (
|
||||
RCTResponseSenderBlock)successCallback errorCallback : (RCTResponseSenderBlock)errorCallback)
|
||||
{
|
||||
CGRect rect = {
|
||||
[RCTConvert CGPoint:@{
|
||||
|
||||
@@ -331,9 +331,9 @@ static RCTImageLoaderCancellationBlock RCTLoadImageURLFromLoader(
|
||||
RCTImageLoaderPartialLoadBlock partialLoadHandler,
|
||||
RCTImageLoaderCompletionBlockWithMetadata completionHandler)
|
||||
{
|
||||
if ([loadHandler respondsToSelector:@selector(loadImageForURL:
|
||||
size:scale:resizeMode:progressHandler:partialLoadHandler
|
||||
:completionHandlerWithMetadata:)]) {
|
||||
if ([loadHandler
|
||||
respondsToSelector:@selector
|
||||
(loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:)]) {
|
||||
return [loadHandler loadImageForURL:imageURL
|
||||
size:size
|
||||
scale:scale
|
||||
@@ -410,17 +410,17 @@ static RCTImageLoaderCancellationBlock RCTLoadImageURLFromLoader(
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||
{
|
||||
RCTImageURLLoaderRequest *request = [self loadImageWithURLRequest:imageURLRequest
|
||||
size:size
|
||||
scale:scale
|
||||
clipped:clipped
|
||||
resizeMode:resizeMode
|
||||
priority:priority
|
||||
attribution:{}
|
||||
progressBlock:progressBlock
|
||||
partialLoadBlock:partialLoadBlock
|
||||
completionBlock:^(NSError *error, UIImage *image, id metadata) {
|
||||
completionBlock(error, image);
|
||||
}];
|
||||
size:size
|
||||
scale:scale
|
||||
clipped:clipped
|
||||
resizeMode:resizeMode
|
||||
priority:priority
|
||||
attribution:{}
|
||||
progressBlock:progressBlock
|
||||
partialLoadBlock:partialLoadBlock
|
||||
completionBlock:^(NSError *error, UIImage *image, id metadata) {
|
||||
completionBlock(error, image);
|
||||
}];
|
||||
return ^{
|
||||
[request cancel];
|
||||
};
|
||||
@@ -1229,10 +1229,8 @@ static RCTImageLoaderCancellationBlock RCTLoadImageURLFromLoader(
|
||||
return std::make_shared<facebook::react::NativeImageLoaderIOSSpecJSI>(params);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getSize
|
||||
: (NSString *)uri resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
getSize : (NSString *)uri resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSURLRequest *request = [RCTConvert NSURLRequest:uri];
|
||||
[self getImageSizeForURLRequest:request
|
||||
@@ -1248,11 +1246,9 @@ RCT_EXPORT_METHOD(getSize
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getSizeWithHeaders
|
||||
: (NSString *)uri headers
|
||||
: (NSDictionary *)headers resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
getSizeWithHeaders : (NSString *)uri headers : (NSDictionary *)headers resolve : (RCTPromiseResolveBlock)
|
||||
resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSURL *URL = [RCTConvert NSURL:uri];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
|
||||
@@ -1269,20 +1265,15 @@ RCT_EXPORT_METHOD(getSizeWithHeaders
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(prefetchImage
|
||||
: (NSString *)uri resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
prefetchImage : (NSString *)uri resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[self prefetchImageWithMetadata:uri queryRootName:nil rootTag:0 resolve:resolve reject:reject];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(prefetchImageWithMetadata
|
||||
: (NSString *)uri queryRootName
|
||||
: (NSString *)queryRootName rootTag
|
||||
: (double)rootTag resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
prefetchImageWithMetadata : (NSString *)uri queryRootName : (NSString *)queryRootName rootTag : (double)
|
||||
rootTag resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSURLRequest *request = [RCTConvert NSURLRequest:uri];
|
||||
[self loadImageWithURLRequest:request
|
||||
@@ -1306,10 +1297,8 @@ RCT_EXPORT_METHOD(prefetchImageWithMetadata
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(queryCache
|
||||
: (NSArray *)uris resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
queryCache : (NSArray *)uris resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
resolve([self getImageCacheStatus:uris]);
|
||||
}
|
||||
|
||||
@@ -106,10 +106,9 @@ RCT_EXPORT_METHOD(hasImageForTag : (NSString *)imageTag callback : (RCTResponseS
|
||||
}
|
||||
|
||||
// TODO (#5906496): Name could be more explicit - something like getBase64EncodedDataForTag:?
|
||||
RCT_EXPORT_METHOD(getBase64ForTag
|
||||
: (NSString *)imageTag successCallback
|
||||
: (RCTResponseSenderBlock)successCallback errorCallback
|
||||
: (RCTResponseSenderBlock)errorCallback)
|
||||
RCT_EXPORT_METHOD(
|
||||
getBase64ForTag : (NSString *)imageTag successCallback : (RCTResponseSenderBlock)
|
||||
successCallback errorCallback : (RCTResponseSenderBlock)errorCallback)
|
||||
{
|
||||
NSData *imageData = _store[imageTag];
|
||||
if (imageData == nullptr) {
|
||||
@@ -123,10 +122,9 @@ RCT_EXPORT_METHOD(getBase64ForTag
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(addImageFromBase64
|
||||
: (NSString *)base64String successCallback
|
||||
: (RCTResponseSenderBlock)successCallback errorCallback
|
||||
: (RCTResponseSenderBlock)errorCallback)
|
||||
RCT_EXPORT_METHOD(
|
||||
addImageFromBase64 : (NSString *)base64String successCallback : (RCTResponseSenderBlock)
|
||||
successCallback errorCallback : (RCTResponseSenderBlock)errorCallback)
|
||||
|
||||
{
|
||||
// Dispatching to a background thread to perform base64 decoding
|
||||
|
||||
@@ -101,12 +101,11 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, CGFloat destScale, RCTR
|
||||
sourceSize.height = destSize.height;
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
}
|
||||
return (CGRect){
|
||||
{
|
||||
RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale),
|
||||
RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale),
|
||||
},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
return (CGRect){{
|
||||
RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale),
|
||||
RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale),
|
||||
},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
|
||||
case RCTResizeModeCover:
|
||||
|
||||
@@ -115,17 +114,16 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, CGFloat destScale, RCTR
|
||||
sourceSize.height = destSize.height;
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
destSize.width = destSize.height * targetAspect;
|
||||
return (CGRect){
|
||||
{RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale), 0}, RCTCeilSize(sourceSize, destScale)};
|
||||
return (CGRect){{RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale), 0},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
sourceSize.width = destSize.width;
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
destSize.height = destSize.width / targetAspect;
|
||||
return (CGRect){
|
||||
{0, RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale)},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
return (CGRect){{0, RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale)},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
}
|
||||
|
||||
case RCTResizeModeCenter:
|
||||
@@ -140,12 +138,11 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, CGFloat destScale, RCTR
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
}
|
||||
|
||||
return (CGRect){
|
||||
{
|
||||
RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale),
|
||||
RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale),
|
||||
},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
return (CGRect){{
|
||||
RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale),
|
||||
RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale),
|
||||
},
|
||||
RCTCeilSize(sourceSize, destScale)};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,10 +53,9 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView)
|
||||
view.renderingMode = json ? UIImageRenderingModeAlwaysTemplate : defaultView.renderingMode;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getSize
|
||||
: (NSURLRequest *)request successBlock
|
||||
: (RCTResponseSenderBlock)successBlock errorBlock
|
||||
: (RCTResponseErrorBlock)errorBlock)
|
||||
RCT_EXPORT_METHOD(
|
||||
getSize : (NSURLRequest *)request successBlock : (RCTResponseSenderBlock)
|
||||
successBlock errorBlock : (RCTResponseErrorBlock)errorBlock)
|
||||
{
|
||||
[[self.bridge moduleForName:@"ImageLoader"
|
||||
lazilyLoadIfNecessary:YES] getImageSizeForURLRequest:request
|
||||
@@ -69,10 +68,9 @@ RCT_EXPORT_METHOD(getSize
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getSizeWithHeaders
|
||||
: (RCTImageSource *)source resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
getSizeWithHeaders : (RCTImageSource *)source resolve : (RCTPromiseResolveBlock)
|
||||
resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[[self.bridge moduleForName:@"ImageLoader" lazilyLoadIfNecessary:YES]
|
||||
getImageSizeForURLRequest:source.request
|
||||
@@ -85,10 +83,9 @@ RCT_EXPORT_METHOD(getSizeWithHeaders
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(prefetchImage
|
||||
: (NSURLRequest *)request resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
prefetchImage : (NSURLRequest *)request resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)
|
||||
reject)
|
||||
{
|
||||
if (!request) {
|
||||
reject(@"E_INVALID_URI", @"Cannot prefetch an image for an empty URI", nil);
|
||||
@@ -107,10 +104,8 @@ RCT_EXPORT_METHOD(prefetchImage
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(queryCache
|
||||
: (NSArray *)requests resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
queryCache : (NSArray *)requests resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
resolve([[self.bridge moduleForName:@"ImageLoader"] getImageCacheStatus:requests]);
|
||||
}
|
||||
|
||||
@@ -87,10 +87,8 @@ RCT_EXPORT_MODULE()
|
||||
[self sendEventWithName:@"url" body:notification.userInfo];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openURL
|
||||
: (NSURL *)URL resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
openURL : (NSURL *)URL resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[RCTSharedApplication() openURL:URL
|
||||
options:@{}
|
||||
@@ -114,10 +112,8 @@ RCT_EXPORT_METHOD(openURL
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(canOpenURL
|
||||
: (NSURL *)URL resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (__unused RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
canOpenURL : (NSURL *)URL resolve : (RCTPromiseResolveBlock)resolve reject : (__unused RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
if (RCTRunningInAppExtension()) {
|
||||
// Technically Today widgets can open urls, but supporting that would require
|
||||
@@ -181,11 +177,9 @@ RCT_EXPORT_METHOD(openSettings : (RCTPromiseResolveBlock)resolve reject : (__unu
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(sendIntent
|
||||
: (NSString *)action extras
|
||||
: (NSArray *_Nullable)extras resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
sendIntent : (NSString *)action extras : (NSArray *_Nullable)extras resolve : (RCTPromiseResolveBlock)
|
||||
resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function Ansi({
|
||||
<View style={styles.container}>
|
||||
{parsedLines.map((items, i) => (
|
||||
<View style={styles.line} key={i}>
|
||||
<Text>
|
||||
<Text style={styles.text}>
|
||||
{items.map((bundle, key) => {
|
||||
const textStyle =
|
||||
bundle.fg && COLORS[bundle.fg]
|
||||
@@ -122,4 +122,7 @@ const styles = StyleSheet.create({
|
||||
line: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
text: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -118,11 +118,9 @@ RCT_EXPORT_METHOD(disconnectAnimatedNodes : (double)parentTag childTag : (double
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startAnimatingNode
|
||||
: (double)animationId nodeTag
|
||||
: (double)nodeTag config
|
||||
: (NSDictionary<NSString *, id> *)config endCallback
|
||||
: (RCTResponseSenderBlock)callBack)
|
||||
RCT_EXPORT_METHOD(
|
||||
startAnimatingNode : (double)animationId nodeTag : (double)nodeTag config : (NSDictionary<NSString *, id> *)
|
||||
config endCallback : (RCTResponseSenderBlock)callBack)
|
||||
{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId]
|
||||
@@ -228,10 +226,9 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue : (double)tag)
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(addAnimatedEventToView
|
||||
: (double)viewTag eventName
|
||||
: (nonnull NSString *)eventName eventMapping
|
||||
: (JS::NativeAnimatedModule::EventMapping &)eventMapping)
|
||||
RCT_EXPORT_METHOD(
|
||||
addAnimatedEventToView : (double)viewTag eventName : (nonnull NSString *)
|
||||
eventName eventMapping : (JS::NativeAnimatedModule::EventMapping &)eventMapping)
|
||||
{
|
||||
NSMutableDictionary *eventMappingDict = [NSMutableDictionary new];
|
||||
eventMappingDict[@"nativeEventPath"] = RCTConvertVecToArray(eventMapping.nativeEventPath());
|
||||
@@ -247,10 +244,9 @@ RCT_EXPORT_METHOD(addAnimatedEventToView
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeAnimatedEventFromView
|
||||
: (double)viewTag eventName
|
||||
: (nonnull NSString *)eventName animatedNodeTag
|
||||
: (double)animatedNodeTag)
|
||||
RCT_EXPORT_METHOD(
|
||||
removeAnimatedEventFromView : (double)viewTag eventName : (nonnull NSString *)eventName animatedNodeTag : (double)
|
||||
animatedNodeTag)
|
||||
{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag]
|
||||
|
||||
@@ -108,11 +108,9 @@ RCT_EXPORT_METHOD(disconnectAnimatedNodes : (double)parentTag childTag : (double
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startAnimatingNode
|
||||
: (double)animationId nodeTag
|
||||
: (double)nodeTag config
|
||||
: (NSDictionary<NSString *, id> *)config endCallback
|
||||
: (RCTResponseSenderBlock)callBack)
|
||||
RCT_EXPORT_METHOD(
|
||||
startAnimatingNode : (double)animationId nodeTag : (double)nodeTag config : (NSDictionary<NSString *, id> *)
|
||||
config endCallback : (RCTResponseSenderBlock)callBack)
|
||||
{
|
||||
[self queueFlushedOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId]
|
||||
@@ -204,10 +202,9 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue : (double)tag)
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(addAnimatedEventToView
|
||||
: (double)viewTag eventName
|
||||
: (nonnull NSString *)eventName eventMapping
|
||||
: (JS::NativeAnimatedModule::EventMapping &)eventMapping)
|
||||
RCT_EXPORT_METHOD(
|
||||
addAnimatedEventToView : (double)viewTag eventName : (nonnull NSString *)
|
||||
eventName eventMapping : (JS::NativeAnimatedModule::EventMapping &)eventMapping)
|
||||
{
|
||||
NSMutableDictionary *eventMappingDict = [NSMutableDictionary new];
|
||||
eventMappingDict[@"nativeEventPath"] = RCTConvertVecToArray(eventMapping.nativeEventPath());
|
||||
@@ -223,10 +220,9 @@ RCT_EXPORT_METHOD(addAnimatedEventToView
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeAnimatedEventFromView
|
||||
: (double)viewTag eventName
|
||||
: (nonnull NSString *)eventName animatedNodeTag
|
||||
: (double)animatedNodeTag)
|
||||
RCT_EXPORT_METHOD(
|
||||
removeAnimatedEventFromView : (double)viewTag eventName : (nonnull NSString *)eventName animatedNodeTag : (double)
|
||||
animatedNodeTag)
|
||||
{
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag]
|
||||
|
||||
@@ -182,6 +182,15 @@ const validAttributesForNonEventProps = {
|
||||
experimental_backgroundImage: ReactNativeFeatureFlags.enableNativeCSSParsing()
|
||||
? (true as const)
|
||||
: {process: require('../StyleSheet/processBackgroundImage').default},
|
||||
experimental_backgroundSize: {
|
||||
process: require('../StyleSheet/processBackgroundSize').default,
|
||||
},
|
||||
experimental_backgroundPosition: {
|
||||
process: require('../StyleSheet/processBackgroundPosition').default,
|
||||
},
|
||||
experimental_backgroundRepeat: {
|
||||
process: require('../StyleSheet/processBackgroundRepeat').default,
|
||||
},
|
||||
boxShadow: ReactNativeFeatureFlags.enableNativeCSSParsing()
|
||||
? (true as const)
|
||||
: {process: require('../StyleSheet/processBoxShadow').default},
|
||||
|
||||
+3
-1
@@ -23,7 +23,9 @@ export function unstable_hasComponent(name: string): boolean {
|
||||
hasNativeComponent = global.__nativeComponentRegistry__hasComponent(name);
|
||||
componentNameToExists.set(name, hasNativeComponent);
|
||||
} else {
|
||||
throw `unstable_hasComponent('${name}'): Global function is not registered`;
|
||||
throw new Error(
|
||||
`unstable_hasComponent('${name}'): Global function is not registered`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return hasNativeComponent;
|
||||
|
||||
@@ -797,9 +797,9 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
#pragma mark - JS API
|
||||
|
||||
RCT_EXPORT_METHOD(sendRequest
|
||||
: (JS::NativeNetworkingIOS::SpecSendRequestQuery &)query callback
|
||||
: (RCTResponseSenderBlock)responseSender)
|
||||
RCT_EXPORT_METHOD(
|
||||
sendRequest : (JS::NativeNetworkingIOS::SpecSendRequestQuery &)query callback : (RCTResponseSenderBlock)
|
||||
responseSender)
|
||||
{
|
||||
NSString *method = query.method();
|
||||
NSString *url = query.url();
|
||||
|
||||
+1
-39
@@ -14,7 +14,6 @@ import type {
|
||||
EventCallback,
|
||||
EventListener,
|
||||
} from '../../src/private/webapis/dom/events/EventTarget';
|
||||
import type Performance from '../../src/private/webapis/performance/Performance';
|
||||
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
|
||||
|
||||
import Event from '../../src/private/webapis/dom/events/Event';
|
||||
@@ -34,13 +33,7 @@ const RCTNetworking = require('./RCTNetworking').default;
|
||||
const base64 = require('base64-js');
|
||||
const invariant = require('invariant');
|
||||
|
||||
const PERFORMANCE_TRACK_NAME = 'Network (JS-initiated only)';
|
||||
const PERFORMANCE_TRACK_GROUP = 'Chrome DevTools Temp Compat';
|
||||
|
||||
declare var performance: Performance;
|
||||
|
||||
const DEBUG_NETWORK_SEND_DELAY: false = false; // Set to a number of milliseconds when debugging
|
||||
const LABEL_FOR_MISSING_URL_FOR_PROFILING = 'Unknown URL';
|
||||
|
||||
export type NativeResponseType = 'base64' | 'blob' | 'text';
|
||||
export type ResponseType =
|
||||
@@ -141,7 +134,6 @@ class XMLHttpRequest extends EventTarget {
|
||||
static DONE: number = DONE;
|
||||
|
||||
static _interceptor: ?XHRInterceptor = null;
|
||||
static _profiling: boolean = false;
|
||||
|
||||
UNSENT: number = UNSENT;
|
||||
OPENED: number = OPENED;
|
||||
@@ -182,10 +174,6 @@ class XMLHttpRequest extends EventTarget {
|
||||
XMLHttpRequest._interceptor = interceptor;
|
||||
}
|
||||
|
||||
static enableProfiling(enableProfiling: boolean): void {
|
||||
XMLHttpRequest._profiling = enableProfiling;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._reset();
|
||||
@@ -389,23 +377,12 @@ class XMLHttpRequest extends EventTarget {
|
||||
return;
|
||||
}
|
||||
|
||||
const start = XMLHttpRequest._profiling ? performance.now() : undefined;
|
||||
|
||||
if (!this._response) {
|
||||
this._response = responseText;
|
||||
} else {
|
||||
this._response += responseText;
|
||||
}
|
||||
|
||||
if (XMLHttpRequest._profiling) {
|
||||
console.timeStamp(
|
||||
'Incremental Data: ' + this._getMeasureURL(),
|
||||
start,
|
||||
undefined,
|
||||
PERFORMANCE_TRACK_NAME,
|
||||
PERFORMANCE_TRACK_GROUP,
|
||||
);
|
||||
}
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.dataReceived(requestId, responseText);
|
||||
|
||||
@@ -450,16 +427,7 @@ class XMLHttpRequest extends EventTarget {
|
||||
this._clearSubscriptions();
|
||||
this._requestId = null;
|
||||
this.setReadyState(this.DONE);
|
||||
if (XMLHttpRequest._profiling && this._startTime != null) {
|
||||
const start = this._startTime;
|
||||
console.timeStamp(
|
||||
this._getMeasureURL(),
|
||||
start,
|
||||
undefined,
|
||||
PERFORMANCE_TRACK_NAME,
|
||||
PERFORMANCE_TRACK_GROUP,
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.loadingFailed(requestId, error);
|
||||
@@ -729,12 +697,6 @@ class XMLHttpRequest extends EventTarget {
|
||||
super.addEventListener(type, listener);
|
||||
}
|
||||
|
||||
_getMeasureURL(): string {
|
||||
return (
|
||||
this._trackingName ?? this._url ?? LABEL_FOR_MISSING_URL_FOR_PROFILING
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* `on<event>` event handling (without JS prototype magic).
|
||||
*/
|
||||
|
||||
@@ -8,15 +8,26 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
|
||||
import Pressability, {
|
||||
type EventHandlers,
|
||||
type PressabilityConfig,
|
||||
} from './Pressability';
|
||||
import {useEffect, useRef} from 'react';
|
||||
import {useEffect, useInsertionEffect, useRef} from 'react';
|
||||
|
||||
declare function usePressability(config: PressabilityConfig): EventHandlers;
|
||||
declare function usePressability(config: null | void): null | EventHandlers;
|
||||
|
||||
// Experiments with using `useInsertionEffect` instead of `useEffect`, which
|
||||
// changes whether `Pressability` is configured or reset when inm a hidden
|
||||
// Activity. With `useInsertionEffect`, `Pressability` behaves more like a
|
||||
// platform control (e.g. Pointer Events), especially with respect to events
|
||||
// like focus and blur.
|
||||
const useConfigurationEffect =
|
||||
ReactNativeFeatureFlags.configurePressabilityDuringInsertion()
|
||||
? useInsertionEffect
|
||||
: useEffect;
|
||||
|
||||
/**
|
||||
* Creates a persistent instance of `Pressability` that automatically configures
|
||||
* itself and resets. Accepts null `config` to support lazy initialization. Once
|
||||
@@ -40,7 +51,7 @@ export default function usePressability(
|
||||
|
||||
// On the initial mount, this is a no-op. On updates, `pressability` will be
|
||||
// re-configured to use the new configuration.
|
||||
useEffect(() => {
|
||||
useConfigurationEffect(() => {
|
||||
if (config != null && pressability != null) {
|
||||
pressability.configure(config);
|
||||
}
|
||||
@@ -48,7 +59,7 @@ export default function usePressability(
|
||||
|
||||
// On unmount, reset pending state and timers inside `pressability`. This is
|
||||
// a separate effect because we do not want to reset when `config` changes.
|
||||
useEffect(() => {
|
||||
useConfigurationEffect(() => {
|
||||
if (pressability != null) {
|
||||
return () => {
|
||||
pressability.reset();
|
||||
|
||||
@@ -327,10 +327,9 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber : (RCTResponseSenderBlock)callba
|
||||
callback(@[ @(RCTSharedApplication().applicationIconBadgeNumber) ]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(requestPermissions
|
||||
: (JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission &)permissions resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
requestPermissions : (JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission &)
|
||||
permissions resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
if (RCTRunningInAppExtension()) {
|
||||
reject(
|
||||
@@ -506,9 +505,8 @@ RCT_EXPORT_METHOD(cancelLocalNotifications : (NSDictionary<NSString *, id> *)use
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialNotification
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (__unused RCTPromiseRejectBlock)reject)
|
||||
RCT_EXPORT_METHOD(
|
||||
getInitialNotification : (RCTPromiseResolveBlock)resolve reject : (__unused RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
// The user actioned a local or remote notification to launch the app. Notification is represented by UNNotification.
|
||||
// Set this property in the implementation of
|
||||
|
||||
Vendored
-152
@@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {
|
||||
HostInstance,
|
||||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
MeasureOnSuccessCallback,
|
||||
NativeMethods,
|
||||
} from '../../../src/private/types/HostInstance';
|
||||
import type {
|
||||
InternalInstanceHandle,
|
||||
ViewConfig,
|
||||
} from '../../Renderer/shims/ReactNativeTypes';
|
||||
|
||||
import TextInputState from '../../Components/TextInput/TextInputState';
|
||||
import {getNodeFromInternalInstanceHandle} from '../../ReactNative/RendererProxy';
|
||||
import {getFabricUIManager} from '../FabricUIManager';
|
||||
import {create} from './ReactNativeAttributePayload';
|
||||
import warnForStyleProps from './warnForStyleProps';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
const {
|
||||
measure: fabricMeasure,
|
||||
measureInWindow: fabricMeasureInWindow,
|
||||
measureLayout: fabricMeasureLayout,
|
||||
getBoundingClientRect: fabricGetBoundingClientRect,
|
||||
setNativeProps,
|
||||
} = nullthrows(getFabricUIManager());
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
/**
|
||||
* This is used for refs on host components.
|
||||
*/
|
||||
export default class ReactFabricHostComponent implements NativeMethods {
|
||||
// These need to be accessible from `ReactFabricPublicInstanceUtils`.
|
||||
__nativeTag: number;
|
||||
__internalInstanceHandle: InternalInstanceHandle;
|
||||
|
||||
_viewConfig: ViewConfig;
|
||||
|
||||
constructor(
|
||||
tag: number,
|
||||
viewConfig: ViewConfig,
|
||||
internalInstanceHandle: InternalInstanceHandle,
|
||||
) {
|
||||
this.__nativeTag = tag;
|
||||
this._viewConfig = viewConfig;
|
||||
this.__internalInstanceHandle = internalInstanceHandle;
|
||||
}
|
||||
|
||||
blur() {
|
||||
// $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement
|
||||
TextInputState.blurTextInput(this);
|
||||
}
|
||||
|
||||
focus() {
|
||||
// $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement
|
||||
TextInputState.focusTextInput(this);
|
||||
}
|
||||
|
||||
measure(callback: MeasureOnSuccessCallback) {
|
||||
const node = getNodeFromInternalInstanceHandle(
|
||||
this.__internalInstanceHandle,
|
||||
);
|
||||
if (node != null) {
|
||||
fabricMeasure(node, callback);
|
||||
}
|
||||
}
|
||||
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
|
||||
const node = getNodeFromInternalInstanceHandle(
|
||||
this.__internalInstanceHandle,
|
||||
);
|
||||
if (node != null) {
|
||||
fabricMeasureInWindow(node, callback);
|
||||
}
|
||||
}
|
||||
|
||||
measureLayout(
|
||||
relativeToNativeNode: number | HostInstance,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail?: () => void /* currently unused */,
|
||||
) {
|
||||
if (
|
||||
typeof relativeToNativeNode === 'number' ||
|
||||
!(relativeToNativeNode instanceof ReactFabricHostComponent)
|
||||
) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: ref.measureLayout must be called with a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const toStateNode = getNodeFromInternalInstanceHandle(
|
||||
this.__internalInstanceHandle,
|
||||
);
|
||||
const fromStateNode = getNodeFromInternalInstanceHandle(
|
||||
relativeToNativeNode.__internalInstanceHandle,
|
||||
);
|
||||
|
||||
if (toStateNode != null && fromStateNode != null) {
|
||||
fabricMeasureLayout(
|
||||
toStateNode,
|
||||
fromStateNode,
|
||||
onFail != null ? onFail : noop,
|
||||
onSuccess != null ? onSuccess : noop,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unstable_getBoundingClientRect(): DOMRect {
|
||||
const node = getNodeFromInternalInstanceHandle(
|
||||
this.__internalInstanceHandle,
|
||||
);
|
||||
if (node != null) {
|
||||
const rect = fabricGetBoundingClientRect(node, true);
|
||||
|
||||
if (rect) {
|
||||
return new DOMRect(rect[0], rect[1], rect[2], rect[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty rect if any of the above failed
|
||||
return new DOMRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
setNativeProps(nativeProps: {...}): void {
|
||||
if (__DEV__) {
|
||||
warnForStyleProps(nativeProps, this._viewConfig.validAttributes);
|
||||
}
|
||||
const updatePayload = create(nativeProps, this._viewConfig.validAttributes);
|
||||
|
||||
const node = getNodeFromInternalInstanceHandle(
|
||||
this.__internalInstanceHandle,
|
||||
);
|
||||
if (node != null && updatePayload != null) {
|
||||
setNativeProps(node, updatePayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+20
-82
@@ -13,124 +13,62 @@
|
||||
* instances and get some data from them (like their instance handle / fiber).
|
||||
*/
|
||||
|
||||
import type ReactNativeDocumentT from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
|
||||
import typeof * as ReactNativeDocumentModuleT from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
|
||||
import type ReactNativeElementT from '../../../src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
import type ReadOnlyTextT from '../../../src/private/webapis/dom/nodes/ReadOnlyText';
|
||||
import typeof * as RendererProxyT from '../../ReactNative/RendererProxy';
|
||||
import type {
|
||||
InternalInstanceHandle,
|
||||
Node,
|
||||
ViewConfig,
|
||||
} from '../../Renderer/shims/ReactNativeTypes';
|
||||
import type {RootTag} from '../RootTag';
|
||||
import type ReactFabricHostComponentT from './ReactFabricHostComponent';
|
||||
|
||||
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
|
||||
import ReactNativeDocument, {
|
||||
createReactNativeDocument,
|
||||
} from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
|
||||
import ReactNativeElement from '../../../src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
import ReadOnlyText from '../../../src/private/webapis/dom/nodes/ReadOnlyText';
|
||||
import * as RendererProxy from '../../ReactNative/RendererProxy';
|
||||
|
||||
export opaque type PublicRootInstance = mixed;
|
||||
|
||||
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
|
||||
let ReactNativeDocumentModuleObject: ?ReactNativeDocumentModuleT;
|
||||
let ReactFabricHostComponentClass: Class<ReactFabricHostComponentT>;
|
||||
let ReactNativeElementClass: Class<ReactNativeElementT>;
|
||||
let ReadOnlyTextClass: Class<ReadOnlyTextT>;
|
||||
let RendererProxy: RendererProxyT;
|
||||
|
||||
function getReactNativeDocumentModule(): ReactNativeDocumentModuleT {
|
||||
if (ReactNativeDocumentModuleObject == null) {
|
||||
// We initialize this lazily to avoid a require cycle.
|
||||
ReactNativeDocumentModuleObject = require('../../../src/private/webapis/dom/nodes/ReactNativeDocument');
|
||||
}
|
||||
|
||||
return ReactNativeDocumentModuleObject;
|
||||
}
|
||||
|
||||
function getReactNativeElementClass(): Class<ReactNativeElementT> {
|
||||
if (ReactNativeElementClass == null) {
|
||||
ReactNativeElementClass =
|
||||
require('../../../src/private/webapis/dom/nodes/ReactNativeElement').default;
|
||||
}
|
||||
return ReactNativeElementClass;
|
||||
}
|
||||
|
||||
function getReactFabricHostComponentClass(): Class<ReactFabricHostComponentT> {
|
||||
if (ReactFabricHostComponentClass == null) {
|
||||
ReactFabricHostComponentClass =
|
||||
require('./ReactFabricHostComponent').default;
|
||||
}
|
||||
return ReactFabricHostComponentClass;
|
||||
}
|
||||
|
||||
function getReadOnlyTextClass(): Class<ReadOnlyTextT> {
|
||||
if (ReadOnlyTextClass == null) {
|
||||
ReadOnlyTextClass =
|
||||
require('../../../src/private/webapis/dom/nodes/ReadOnlyText').default;
|
||||
}
|
||||
return ReadOnlyTextClass;
|
||||
}
|
||||
|
||||
export function createPublicRootInstance(rootTag: RootTag): PublicRootInstance {
|
||||
if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) {
|
||||
const ReactNativeDocumentModule = getReactNativeDocumentModule();
|
||||
|
||||
// $FlowExpectedError[incompatible-return]
|
||||
return ReactNativeDocumentModule.createReactNativeDocument(rootTag);
|
||||
}
|
||||
|
||||
// $FlowExpectedError[incompatible-return]
|
||||
return null;
|
||||
return createReactNativeDocument(rootTag);
|
||||
}
|
||||
|
||||
export function createPublicInstance(
|
||||
tag: number,
|
||||
viewConfig: ViewConfig,
|
||||
internalInstanceHandle: InternalInstanceHandle,
|
||||
ownerDocument: ReactNativeDocumentT,
|
||||
): ReactFabricHostComponentT | ReactNativeElementT {
|
||||
if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) {
|
||||
const ReactNativeElement = getReactNativeElementClass();
|
||||
return new ReactNativeElement(
|
||||
tag,
|
||||
viewConfig,
|
||||
internalInstanceHandle,
|
||||
ownerDocument,
|
||||
);
|
||||
} else {
|
||||
const ReactFabricHostComponent = getReactFabricHostComponentClass();
|
||||
return new ReactFabricHostComponent(
|
||||
tag,
|
||||
viewConfig,
|
||||
internalInstanceHandle,
|
||||
);
|
||||
}
|
||||
ownerDocument: ReactNativeDocument,
|
||||
): ReactNativeElement {
|
||||
return new ReactNativeElement(
|
||||
tag,
|
||||
viewConfig,
|
||||
internalInstanceHandle,
|
||||
ownerDocument,
|
||||
);
|
||||
}
|
||||
|
||||
export function createPublicTextInstance(
|
||||
internalInstanceHandle: InternalInstanceHandle,
|
||||
ownerDocument: ReactNativeDocumentT,
|
||||
): ReadOnlyTextT {
|
||||
const ReadOnlyText = getReadOnlyTextClass();
|
||||
ownerDocument: ReactNativeDocument,
|
||||
): ReadOnlyText {
|
||||
return new ReadOnlyText(internalInstanceHandle, ownerDocument);
|
||||
}
|
||||
|
||||
export function getNativeTagFromPublicInstance(
|
||||
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
|
||||
publicInstance: ReactNativeElement,
|
||||
): number {
|
||||
return publicInstance.__nativeTag;
|
||||
}
|
||||
|
||||
export function getNodeFromPublicInstance(
|
||||
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
|
||||
publicInstance: ReactNativeElement,
|
||||
): ?Node {
|
||||
// Avoid loading ReactFabric if using an instance from the legacy renderer.
|
||||
if (publicInstance.__internalInstanceHandle == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (RendererProxy == null) {
|
||||
RendererProxy = require('../../ReactNative/RendererProxy');
|
||||
}
|
||||
return RendererProxy.getNodeFromInternalInstanceHandle(
|
||||
// $FlowExpectedError[incompatible-type] __internalInstanceHandle is always an InternalInstanceHandle from React when we get here.
|
||||
publicInstance.__internalInstanceHandle,
|
||||
@@ -138,7 +76,7 @@ export function getNodeFromPublicInstance(
|
||||
}
|
||||
|
||||
export function getInternalInstanceHandleFromPublicInstance(
|
||||
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
|
||||
publicInstance: ReactNativeElement,
|
||||
): InternalInstanceHandle {
|
||||
// TODO(T174762768): Remove this once OSS versions of renderers will be synced.
|
||||
// $FlowExpectedError[prop-missing] Keeping this for backwards-compatibility with the renderers versions in open source.
|
||||
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import type {
|
||||
InternalInstanceHandle,
|
||||
ViewConfig,
|
||||
} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
|
||||
import type ReactNativeDocument from 'react-native/src/private/webapis/dom/nodes/ReactNativeDocument';
|
||||
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import ReactFabricHostComponent from 'react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent';
|
||||
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
|
||||
// Create fake parameters for the class.
|
||||
const tag = 11;
|
||||
const viewConfig: ViewConfig = {
|
||||
uiViewClassName: 'test',
|
||||
validAttributes: {
|
||||
style: {},
|
||||
},
|
||||
};
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const internalInstanceHandle: InternalInstanceHandle = {};
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const ownerDocument: ReactNativeDocument = {};
|
||||
|
||||
const ITERATIONS = 100;
|
||||
|
||||
const now = Fantom.unstable_benchmark.now;
|
||||
|
||||
/* eslint-disable no-new */
|
||||
Fantom.unstable_benchmark
|
||||
.suite('ReactNativeElement vs. ReactFabricHostComponent', {
|
||||
minIterations: 50000,
|
||||
})
|
||||
.test('ReactNativeElement', () => {
|
||||
// This logic is very fast and if we only run it once the cost of calling
|
||||
// the benchmark function itself is significant. Calling it in a loop and
|
||||
// computing the average is more accurate to account for that.
|
||||
const start = now();
|
||||
for (let i = 0; i < ITERATIONS; i++) {
|
||||
new ReactNativeElement(
|
||||
tag,
|
||||
viewConfig,
|
||||
internalInstanceHandle,
|
||||
ownerDocument,
|
||||
);
|
||||
}
|
||||
const end = now();
|
||||
return {
|
||||
overriddenDuration: (end - start) / ITERATIONS,
|
||||
};
|
||||
})
|
||||
.test('ReactFabricHostComponent', () => {
|
||||
// This logic is very fast and if we only run it once the cost of calling
|
||||
// the benchmark function itself is significant. Calling it in a loop and
|
||||
// computing the average is more accurate to account for that.
|
||||
const start = now();
|
||||
for (let i = 0; i < ITERATIONS; i++) {
|
||||
new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle);
|
||||
}
|
||||
const end = now();
|
||||
return {
|
||||
overriddenDuration: (end - start) / ITERATIONS,
|
||||
};
|
||||
});
|
||||
-353
@@ -1,353 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @fantom_flags enableAccessToHostTreeInFabric:*
|
||||
*/
|
||||
|
||||
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import type {HostInstance} from 'react-native';
|
||||
|
||||
import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
import TextInput from '../../../Components/TextInput/TextInput';
|
||||
import TextInputState from '../../../Components/TextInput/TextInputState';
|
||||
import View from '../../../Components/View/View';
|
||||
import ReactFabricHostComponent from '../ReactFabricHostComponent';
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import nullthrows from 'nullthrows';
|
||||
import * as React from 'react';
|
||||
import {createRef} from 'react';
|
||||
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
|
||||
|
||||
// TODO: move these tests to the test file for `ReactNativeElement` when the legacy implementation is removed.
|
||||
describe('ReactFabricPublicInstance', () => {
|
||||
it('should provide instances of the right class as refs in host components', () => {
|
||||
const nodeRef = createRef<HostInstance>();
|
||||
|
||||
const root = Fantom.createRoot();
|
||||
Fantom.runTask(() => {
|
||||
root.render(<View ref={nodeRef} />);
|
||||
});
|
||||
|
||||
const node = nullthrows(nodeRef.current);
|
||||
|
||||
expect(node).toBeInstanceOf(
|
||||
ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()
|
||||
? ReactNativeElement
|
||||
: ReactFabricHostComponent,
|
||||
);
|
||||
});
|
||||
|
||||
describe('blur', () => {
|
||||
test('blur() invokes TextInputState', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const nodeRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<TextInput ref={nodeRef} />);
|
||||
});
|
||||
|
||||
const node = nullthrows(nodeRef.current);
|
||||
|
||||
const blurTextInput = jest.fn();
|
||||
|
||||
// We don't support view commands in Fantom yet, so we have to mock this.
|
||||
TextInputState.blurTextInput = blurTextInput;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
node.blur();
|
||||
});
|
||||
|
||||
expect(blurTextInput).toHaveBeenCalledTimes(1);
|
||||
expect(blurTextInput.mock.calls).toEqual([[node]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('focus', () => {
|
||||
test('focus() invokes TextInputState', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<TextInput ref={ref} />);
|
||||
});
|
||||
|
||||
const node = nullthrows(ref.current);
|
||||
|
||||
const focusTextInput = jest.fn();
|
||||
|
||||
// We don't support view commands in Fantom yet, so we have to mock this.
|
||||
TextInputState.focusTextInput = focusTextInput;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
node.focus();
|
||||
});
|
||||
|
||||
expect(focusTextInput).toHaveBeenCalledTimes(1);
|
||||
expect(focusTextInput.mock.calls).toEqual([[node]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('measure', () => {
|
||||
it('component.measure(...) invokes callback', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={ref}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
const node = nullthrows(ref.current);
|
||||
|
||||
const callback = jest.fn();
|
||||
node.measure(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 10, 10]]);
|
||||
});
|
||||
|
||||
it('unmounted.measure(...) does nothing', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={ref}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
const node = nullthrows(ref.current);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<></>);
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
node.measure(callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('measureInWindow', () => {
|
||||
it('component.measureInWindow(...) invokes callback', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={ref}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
const node = nullthrows(ref.current);
|
||||
|
||||
const callback = jest.fn();
|
||||
node.measureInWindow(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]);
|
||||
});
|
||||
|
||||
it('unmounted.measureInWindow(...) does nothing', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={ref}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
const node = nullthrows(ref.current);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<></>);
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
node.measureInWindow(callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('measureLayout', () => {
|
||||
it('component.measureLayout(component, ...) invokes callback', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const parentRef = createRef<HostInstance>();
|
||||
const childRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={parentRef}>
|
||||
<View
|
||||
style={{width: 10, height: 10, left: 20, top: 20}}
|
||||
ref={childRef}
|
||||
/>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
const parentNode = nullthrows(parentRef.current);
|
||||
const childNode = nullthrows(childRef.current);
|
||||
|
||||
const callback = jest.fn();
|
||||
childNode.measureLayout(parentNode, callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[20, 20, 10, 10]]);
|
||||
});
|
||||
|
||||
it('unmounted.measureLayout(component, ...) does nothing', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const parentRef = createRef<HostInstance>();
|
||||
const childRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={parentRef}>
|
||||
<View
|
||||
style={{width: 10, height: 10, left: 20, top: 20}}
|
||||
ref={childRef}
|
||||
/>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
const parentNode = nullthrows(parentRef.current);
|
||||
const childNode = nullthrows(childRef.current);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View style={{width: 100, height: 100, left: 10, top: 10}} />,
|
||||
);
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
childNode.measureLayout(parentNode, callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('component.measureLayout(unmounted, ...) does nothing', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const parentRef = createRef<HostInstance>();
|
||||
const childRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={parentRef}>
|
||||
<View
|
||||
style={{width: 10, height: 10, left: 20, top: 20}}
|
||||
ref={childRef}
|
||||
/>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
const parentNode = nullthrows(parentRef.current);
|
||||
const childNode = nullthrows(childRef.current);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View style={{width: 100, height: 100, left: 10, top: 10}} />,
|
||||
);
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
parentNode.measureLayout(childNode, callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('unmounted.measureLayout(unmounted, ...) does nothing', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const parentRef = createRef<HostInstance>();
|
||||
const childRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
style={{width: 100, height: 100, left: 10, top: 10}}
|
||||
ref={parentRef}>
|
||||
<View
|
||||
style={{width: 10, height: 10, left: 20, top: 20}}
|
||||
ref={childRef}
|
||||
/>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
const parentNode = nullthrows(parentRef.current);
|
||||
const childNode = nullthrows(childRef.current);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<></>);
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
childNode.measureLayout(parentNode, callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setNativeProps', () => {
|
||||
it('should propagate changes to the host component', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const nodeRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<View ref={nodeRef} testID="first test id" />);
|
||||
});
|
||||
|
||||
expect(
|
||||
root
|
||||
.getRenderedOutput({
|
||||
props: ['testID'],
|
||||
})
|
||||
.toJSX(),
|
||||
).toEqual(<rn-view testID={'first test id'} />);
|
||||
|
||||
const element = nullthrows(nodeRef.current);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
element.setNativeProps({testID: 'second test id'});
|
||||
});
|
||||
|
||||
expect(
|
||||
root
|
||||
.getRenderedOutput({
|
||||
props: ['testID'],
|
||||
})
|
||||
.toJSX(),
|
||||
).toEqual(<rn-view testID={'second test id'} />);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @fantom_flags updateRuntimeShadowNodeReferencesOnCommit:true
|
||||
* @fantom_react_fb_flags passChildrenWhenCloningPersistedNodes:true
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
describe('Render updates with passChildrenWhenCloningPersistedNodes', () => {
|
||||
it('updates the runtime shadow node reference correctly when using sync on commit', () => {
|
||||
const root = Fantom.createRoot();
|
||||
|
||||
// Initial render setting to row items next to each other
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View nativeID="parent" style={{flex: 1, flexDirection: 'row'}}>
|
||||
<View
|
||||
nativeID="initial-child-1"
|
||||
key="1"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="initial-child-2"
|
||||
key="2"
|
||||
style={{width: 100, height: 32}}>
|
||||
<View nativeID="sub-child-2-1" key="1" />
|
||||
</View>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
// Insert new items on the row between the previous two items
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View nativeID="parent" style={{flex: 1, flexDirection: 'row'}}>
|
||||
<View
|
||||
nativeID="initial-child-1"
|
||||
key="1"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="inserted-child-1"
|
||||
key="3"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="initial-child-2"
|
||||
key="2"
|
||||
style={{width: 100, height: 32}}>
|
||||
<View nativeID="sub-child-2-1" key="1" />
|
||||
</View>
|
||||
<View
|
||||
nativeID="inserted-child-2"
|
||||
key="4"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="inserted-child-3"
|
||||
key="5"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
// Update a prop on the children of the original row item to force a clone
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View nativeID="parent" style={{flex: 1, flexDirection: 'row'}}>
|
||||
<View
|
||||
nativeID="initial-child-1"
|
||||
key="1"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="inserted-child-1"
|
||||
key="3"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="initial-child-2"
|
||||
key="2"
|
||||
style={{width: 100, height: 32}}>
|
||||
<View
|
||||
nativeID="sub-child-2-1"
|
||||
key="1"
|
||||
testID="prop-change-to-force-clone"
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
nativeID="inserted-child-2"
|
||||
key="4"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
<View
|
||||
nativeID="inserted-child-3"
|
||||
key="5"
|
||||
style={{width: 100, height: 32}}
|
||||
/>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
// The row items should all be layed out next to each other
|
||||
expect(
|
||||
root
|
||||
.getRenderedOutput({
|
||||
includeLayoutMetrics: true,
|
||||
props: ['layoutMetrics-frame', 'nativeID'],
|
||||
})
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:0,y:0,width:390,height:844}',
|
||||
nativeID: 'parent',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:0,y:0,width:100,height:32}',
|
||||
nativeID: 'initial-child-1',
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:100,y:0,width:100,height:32}',
|
||||
nativeID: 'inserted-child-1',
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:200,y:0,width:100,height:32}',
|
||||
nativeID: 'initial-child-2',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:0,y:0,width:100,height:0}',
|
||||
nativeID: 'sub-child-2-1',
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:300,y:0,width:100,height:32}',
|
||||
nativeID: 'inserted-child-2',
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
type: 'View',
|
||||
props: {
|
||||
'layoutMetrics-frame': '{x:400,y:0,width:100,height:32}',
|
||||
nativeID: 'inserted-child-3',
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,12 @@ const ReactNativeStyleAttributes =
|
||||
const resolveAssetSource = require('../Image/resolveAssetSource').default;
|
||||
const processBackgroundImage =
|
||||
require('../StyleSheet/processBackgroundImage').default;
|
||||
const processBackgroundPosition =
|
||||
require('../StyleSheet/processBackgroundPosition').default;
|
||||
const processBackgroundRepeat =
|
||||
require('../StyleSheet/processBackgroundRepeat').default;
|
||||
const processBackgroundSize =
|
||||
require('../StyleSheet/processBackgroundSize').default;
|
||||
const processColor = require('../StyleSheet/processColor').default;
|
||||
const processColorArray = require('../StyleSheet/processColorArray').default;
|
||||
const processFilter = require('../StyleSheet/processFilter').default;
|
||||
@@ -203,6 +209,12 @@ function getProcessorForType(typeName: string): ?(nextProp: any) => any {
|
||||
return processFilter;
|
||||
case 'BackgroundImage':
|
||||
return processBackgroundImage;
|
||||
case 'BackgroundPosition':
|
||||
return processBackgroundPosition;
|
||||
case 'BackgroundRepeat':
|
||||
return processBackgroundRepeat;
|
||||
case 'BackgroundSize':
|
||||
return processBackgroundSize;
|
||||
case 'ImageSource':
|
||||
return resolveAssetSource;
|
||||
case 'BoxShadow':
|
||||
|
||||
+2
-9
@@ -7,9 +7,9 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<90e5b8f55761c6a4805052d5b49689b8>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* This file is no longer sync'd from the facebook/react repository.
|
||||
* The version compatability check is removed. Use at your own risk.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -19455,13 +19455,6 @@ __DEV__ &&
|
||||
setSuspenseHandler = function (newShouldSuspendImpl) {
|
||||
shouldSuspendImpl = newShouldSuspendImpl;
|
||||
};
|
||||
var isomorphicReactPackageVersion = React.version;
|
||||
if ("19.2.0" !== isomorphicReactPackageVersion)
|
||||
throw Error(
|
||||
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
|
||||
(isomorphicReactPackageVersion +
|
||||
"\n - react-native-renderer: 19.2.0\nLearn more: https://react.dev/warnings/version-mismatch")
|
||||
);
|
||||
if (
|
||||
"function" !==
|
||||
typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog
|
||||
|
||||
+2
-9
@@ -7,9 +7,9 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<14f9eca680f3065d18eda114fb4bea9f>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* This file is no longer sync'd from the facebook/react repository.
|
||||
* The version compatability check is removed. Use at your own risk.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -10918,13 +10918,6 @@ function updateContainer(element, container, parentComponent, callback) {
|
||||
entangleTransitions(element, parentComponent, lane));
|
||||
return lane;
|
||||
}
|
||||
var isomorphicReactPackageVersion = React.version;
|
||||
if ("19.2.0" !== isomorphicReactPackageVersion)
|
||||
throw Error(
|
||||
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
|
||||
(isomorphicReactPackageVersion +
|
||||
"\n - react-native-renderer: 19.2.0\nLearn more: https://react.dev/warnings/version-mismatch")
|
||||
);
|
||||
if (
|
||||
"function" !==
|
||||
typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog
|
||||
|
||||
+2
-9
@@ -7,9 +7,9 @@
|
||||
* @noflow
|
||||
* @nolint
|
||||
* @preventMunge
|
||||
* @generated SignedSource<<238b7d9e5ffe0f30b64b3b5c7c156088>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* This file is no longer sync'd from the facebook/react repository.
|
||||
* The version compatability check is removed. Use at your own risk.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -12713,13 +12713,6 @@ function updateContainer(element, container, parentComponent, callback) {
|
||||
entangleTransitions(element, parentComponent, lane));
|
||||
return lane;
|
||||
}
|
||||
var isomorphicReactPackageVersion = React.version;
|
||||
if ("19.2.0" !== isomorphicReactPackageVersion)
|
||||
throw Error(
|
||||
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
|
||||
(isomorphicReactPackageVersion +
|
||||
"\n - react-native-renderer: 19.2.0\nLearn more: https://react.dev/warnings/version-mismatch")
|
||||
);
|
||||
if (
|
||||
"function" !==
|
||||
typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
* @noformat
|
||||
* @nolint
|
||||
* @flow
|
||||
* @generated SignedSource<<16b364e89f43b8a47832b0dfb98af11e>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* @generated SignedSource<<cf323fc5ca893bab5669c7d321660412>>
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
* @noformat
|
||||
* @nolint
|
||||
* @flow strict-local
|
||||
* @generated SignedSource<<1dd9e9c3f20e37ae14e485fc6ee3d9e9>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* @generated SignedSource<<908f5fb85384725318e261f40e49d9a6>>
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
* @noformat
|
||||
* @nolint
|
||||
* @flow
|
||||
* @generated SignedSource<<e2c46705ed927302dbe9332dafba459d>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* This file is no longer sync'd from the facebook/react repository.
|
||||
* The version compatability check is removed. Use at your own risk.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
* @noformat
|
||||
* @nolint
|
||||
* @flow strict
|
||||
* @generated SignedSource<<989e6e2e860dc2af7ba983849111bda8>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* @generated SignedSource<<c0e57723772ea5f1aa8c3c897ac3c216>>
|
||||
*/
|
||||
|
||||
import type {
|
||||
|
||||
+1
-3
@@ -7,9 +7,7 @@
|
||||
* @noformat
|
||||
* @nolint
|
||||
* @flow strict-local
|
||||
* @generated SignedSource<<67d18226984338ab9301147ce0a7d414>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* @generated SignedSource<<1f7876c0dc0b05685a730513dc410236>>
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
+1
-3
@@ -7,9 +7,7 @@
|
||||
* @noformat
|
||||
* @nolint
|
||||
* @flow strict-local
|
||||
* @generated SignedSource<<556d1487de0b9e4a09cbc67dd130a884>>
|
||||
*
|
||||
* This file was sync'd from the facebook/react repository.
|
||||
* @generated SignedSource<<52163887de05f1cff05388145cf85b3b>>
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -44,22 +44,22 @@ struct RCTRequired {
|
||||
/// Pass-through constructor (allows for implicit conversion) for wrapped type
|
||||
/// T
|
||||
template <typename... Args>
|
||||
RCTREQUIRED_INLINE RCTRequired(Args&&... args)
|
||||
: _t(std::forward<Args>(args)...) {
|
||||
RCTREQUIRED_INLINE RCTRequired(Args &&...args) : _t(std::forward<Args>(args)...)
|
||||
{
|
||||
static_assert(
|
||||
sizeof...(Args) > 0,
|
||||
"Required struct member not initialized. Expand assert trace to see where this was triggered.");
|
||||
}
|
||||
|
||||
RCTREQUIRED_INLINE
|
||||
RCTRequired(const RCTRequired&) = default;
|
||||
RCTRequired(const RCTRequired &) = default;
|
||||
RCTREQUIRED_INLINE
|
||||
RCTRequired(RCTRequired&&) = default;
|
||||
RCTRequired(RCTRequired &&) = default;
|
||||
|
||||
RCTREQUIRED_INLINE
|
||||
RCTRequired& operator=(const RCTRequired&) = default;
|
||||
RCTRequired &operator=(const RCTRequired &) = default;
|
||||
RCTREQUIRED_INLINE
|
||||
RCTRequired& operator=(RCTRequired&&) = default;
|
||||
RCTRequired &operator=(RCTRequired &&) = default;
|
||||
|
||||
RCTREQUIRED_INLINE
|
||||
~RCTRequired() = default;
|
||||
@@ -67,21 +67,25 @@ struct RCTRequired {
|
||||
/// Public accessor for private storage (Use when implicit conversion is
|
||||
/// impracticable)
|
||||
RCTREQUIRED_INLINE
|
||||
const T& get() const {
|
||||
const T &get() const
|
||||
{
|
||||
return _t;
|
||||
}
|
||||
RCTREQUIRED_INLINE
|
||||
T& get() {
|
||||
T &get()
|
||||
{
|
||||
return _t;
|
||||
}
|
||||
|
||||
// Implicit conversion
|
||||
RCTREQUIRED_INLINE
|
||||
operator T() const {
|
||||
operator T() const
|
||||
{
|
||||
return _t;
|
||||
}
|
||||
RCTREQUIRED_INLINE
|
||||
operator T&() {
|
||||
operator T &()
|
||||
{
|
||||
return _t;
|
||||
}
|
||||
|
||||
|
||||
@@ -779,6 +779,43 @@ type RadialGradientValue = {
|
||||
|
||||
export type BackgroundImageValue = LinearGradientValue | RadialGradientValue;
|
||||
|
||||
export type BackgroundSizeValue =
|
||||
| {
|
||||
x: string | number,
|
||||
y: string | number,
|
||||
}
|
||||
| 'cover'
|
||||
| 'contain';
|
||||
|
||||
export type BackgroundRepeatKeyword =
|
||||
| 'repeat'
|
||||
| 'space'
|
||||
| 'round'
|
||||
| 'no-repeat';
|
||||
|
||||
export type BackgroundPositionValue =
|
||||
| {
|
||||
top: number | string,
|
||||
left: number | string,
|
||||
}
|
||||
| {
|
||||
top: number | string,
|
||||
right: number | string,
|
||||
}
|
||||
| {
|
||||
bottom: number | string,
|
||||
left: number | string,
|
||||
}
|
||||
| {
|
||||
bottom: number | string,
|
||||
right: number | string,
|
||||
};
|
||||
|
||||
export type BackgroundRepeatValue = {
|
||||
x: BackgroundRepeatKeyword,
|
||||
y: BackgroundRepeatKeyword,
|
||||
};
|
||||
|
||||
export type BoxShadowValue = {
|
||||
offsetX: number | string,
|
||||
offsetY: number | string,
|
||||
@@ -853,6 +890,13 @@ export type ____ViewStyle_InternalBase = $ReadOnly<{
|
||||
filter?: $ReadOnlyArray<FilterFunction> | string,
|
||||
mixBlendMode?: ____BlendMode_Internal,
|
||||
experimental_backgroundImage?: $ReadOnlyArray<BackgroundImageValue> | string,
|
||||
experimental_backgroundSize?: $ReadOnlyArray<BackgroundSizeValue> | string,
|
||||
experimental_backgroundPosition?:
|
||||
| $ReadOnlyArray<BackgroundPositionValue>
|
||||
| string,
|
||||
experimental_backgroundRepeat?:
|
||||
| $ReadOnlyArray<BackgroundRepeatValue>
|
||||
| string,
|
||||
isolation?: 'auto' | 'isolate',
|
||||
}>;
|
||||
|
||||
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import processBackgroundPosition from '../processBackgroundPosition';
|
||||
|
||||
describe('processBackgroundPosition', () => {
|
||||
// Test null/undefined input
|
||||
it('should handle null input', () => {
|
||||
expect(processBackgroundPosition(null)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle undefined input', () => {
|
||||
expect(processBackgroundPosition(undefined)).toEqual([]);
|
||||
});
|
||||
|
||||
// Test empty string input
|
||||
it('should handle empty string', () => {
|
||||
expect(processBackgroundPosition('')).toEqual([]);
|
||||
});
|
||||
|
||||
// Test array input
|
||||
it('should handle array input', () => {
|
||||
expect(processBackgroundPosition([{top: '50%', left: '50%'}])).toEqual([
|
||||
{top: '50%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle array with multiple positions', () => {
|
||||
expect(
|
||||
processBackgroundPosition([{bottom: '100%', right: '100%'}]),
|
||||
).toEqual([{bottom: '100%', right: '100%'}]);
|
||||
});
|
||||
|
||||
// Test single keyword values
|
||||
it('should parse left', () => {
|
||||
expect(processBackgroundPosition('left')).toEqual([
|
||||
{top: '50%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse center', () => {
|
||||
expect(processBackgroundPosition('center')).toEqual([
|
||||
{top: '50%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right', () => {
|
||||
expect(processBackgroundPosition('right')).toEqual([
|
||||
{top: '50%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse top', () => {
|
||||
expect(processBackgroundPosition('top')).toEqual([
|
||||
{top: '0%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse bottom', () => {
|
||||
expect(processBackgroundPosition('bottom')).toEqual([
|
||||
{top: '100%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test single length/percentage values
|
||||
it('should parse single px value', () => {
|
||||
expect(processBackgroundPosition('100px')).toEqual([
|
||||
{top: '50%', left: 100},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse single percentage value', () => {
|
||||
expect(processBackgroundPosition('25%')).toEqual([
|
||||
{top: '50%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse single decimal px value', () => {
|
||||
expect(processBackgroundPosition('100.5px')).toEqual([
|
||||
{top: '50%', left: 100.5},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse single decimal percentage value', () => {
|
||||
expect(processBackgroundPosition('25.5%')).toEqual([
|
||||
{top: '50%', left: '25.5%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test two-value syntax
|
||||
it('should parse left top', () => {
|
||||
expect(processBackgroundPosition('left top')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse left center', () => {
|
||||
expect(processBackgroundPosition('left center')).toEqual([
|
||||
{top: '50%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse center top', () => {
|
||||
expect(processBackgroundPosition('center top')).toEqual([
|
||||
{top: '0%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse center center', () => {
|
||||
expect(processBackgroundPosition('center center')).toEqual([
|
||||
{top: '50%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse center bottom', () => {
|
||||
expect(processBackgroundPosition('center bottom')).toEqual([
|
||||
{top: '100%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right center', () => {
|
||||
expect(processBackgroundPosition('right center')).toEqual([
|
||||
{top: '50%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right bottom', () => {
|
||||
expect(processBackgroundPosition('right bottom')).toEqual([
|
||||
{top: '100%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test keyword with length/percentage
|
||||
it('should parse left with px', () => {
|
||||
expect(processBackgroundPosition('left 100px')).toEqual([
|
||||
{top: 100, left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse left with percentage', () => {
|
||||
expect(processBackgroundPosition('left 25%')).toEqual([
|
||||
{top: '25%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right with px', () => {
|
||||
expect(processBackgroundPosition('right 100px')).toEqual([
|
||||
{top: 100, left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right with percentage', () => {
|
||||
expect(processBackgroundPosition('right 25%')).toEqual([
|
||||
{top: '25%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test length/percentage with keyword
|
||||
it('should parse px with top', () => {
|
||||
expect(processBackgroundPosition('100px top')).toEqual([
|
||||
{top: '0%', left: 100},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse px with bottom', () => {
|
||||
expect(processBackgroundPosition('100px bottom')).toEqual([
|
||||
{top: '100%', left: 100},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse percentage with top', () => {
|
||||
expect(processBackgroundPosition('25% top')).toEqual([
|
||||
{top: '0%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse percentage with bottom', () => {
|
||||
expect(processBackgroundPosition('25% bottom')).toEqual([
|
||||
{top: '100%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test two length/percentage values
|
||||
it('should parse px px', () => {
|
||||
expect(processBackgroundPosition('100px 200px')).toEqual([
|
||||
{top: 200, left: 100},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse percentage percentage', () => {
|
||||
expect(processBackgroundPosition('25% 75%')).toEqual([
|
||||
{top: '75%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse px percentage', () => {
|
||||
expect(processBackgroundPosition('100px 75%')).toEqual([
|
||||
{top: '75%', left: 100},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse percentage px', () => {
|
||||
expect(processBackgroundPosition('25% 200px')).toEqual([
|
||||
{top: 200, left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test three-value syntax
|
||||
it('should parse center top with percentage', () => {
|
||||
expect(processBackgroundPosition('center top 25%')).toEqual([
|
||||
{top: '25%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse center bottom with percentage', () => {
|
||||
expect(processBackgroundPosition('center bottom 25%')).toEqual([
|
||||
{bottom: '25%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse left center with percentage', () => {
|
||||
expect(processBackgroundPosition('left 25% center')).toEqual([
|
||||
{top: '50%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right center with percentage', () => {
|
||||
expect(processBackgroundPosition('right 25% center')).toEqual([
|
||||
{top: '50%', right: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse left with percentage top', () => {
|
||||
expect(processBackgroundPosition('left 25% top')).toEqual([
|
||||
{top: '0%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse left with percentage bottom', () => {
|
||||
expect(processBackgroundPosition('left 25% bottom')).toEqual([
|
||||
{bottom: '0%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right with percentage top', () => {
|
||||
expect(processBackgroundPosition('right 25% top')).toEqual([
|
||||
{top: '0%', right: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right with percentage bottom', () => {
|
||||
expect(processBackgroundPosition('right 25% bottom')).toEqual([
|
||||
{bottom: '0%', right: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test four-value syntax
|
||||
it('should parse left percentage top percentage', () => {
|
||||
expect(processBackgroundPosition('left 25% top 75%')).toEqual([
|
||||
{top: '75%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right percentage top percentage', () => {
|
||||
expect(processBackgroundPosition('right 25% top 75%')).toEqual([
|
||||
{top: '75%', right: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse left percentage bottom percentage', () => {
|
||||
expect(processBackgroundPosition('left 25% bottom 75%')).toEqual([
|
||||
{bottom: '75%', left: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse right percentage bottom percentage', () => {
|
||||
expect(processBackgroundPosition('right 25% bottom 75%')).toEqual([
|
||||
{bottom: '75%', right: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test multiple background positions (comma-separated)
|
||||
it('should parse multiple background positions', () => {
|
||||
expect(processBackgroundPosition('left top, right bottom')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
{top: '100%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background positions with values', () => {
|
||||
expect(
|
||||
processBackgroundPosition('left 25% top 75%, center center'),
|
||||
).toEqual([
|
||||
{top: '75%', left: '25%'},
|
||||
{top: '50%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background positions with newlines', () => {
|
||||
expect(processBackgroundPosition('left top,\nright bottom')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
{top: '100%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background positions with extra whitespace', () => {
|
||||
expect(processBackgroundPosition(' left top , right bottom ')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
{top: '100%', left: '100%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test case sensitivity
|
||||
it('should handle mixed case keywords', () => {
|
||||
expect(processBackgroundPosition('LEFT')).toEqual([
|
||||
{top: '50%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed case keywords with spaces', () => {
|
||||
expect(processBackgroundPosition(' Left ')).toEqual([
|
||||
{top: '50%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed case in two values', () => {
|
||||
expect(processBackgroundPosition('LEFT TOP')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed case in unit values', () => {
|
||||
expect(processBackgroundPosition('LEFT 100PX')).toEqual([
|
||||
{top: 100, left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test whitespace handling
|
||||
it('should handle extra spaces between values', () => {
|
||||
expect(processBackgroundPosition('left top')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle tabs and newlines', () => {
|
||||
expect(processBackgroundPosition('left\ttop\n')).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test edge cases and invalid inputs
|
||||
it('should handle invalid single value', () => {
|
||||
expect(processBackgroundPosition('invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid two values', () => {
|
||||
expect(processBackgroundPosition('left invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid first value', () => {
|
||||
expect(processBackgroundPosition('invalid top')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle too many values', () => {
|
||||
expect(processBackgroundPosition('left top center')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty values in comma-separated list', () => {
|
||||
expect(processBackgroundPosition('left top,')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle only comma', () => {
|
||||
expect(processBackgroundPosition(',')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle multiple commas', () => {
|
||||
expect(processBackgroundPosition('left top,,right bottom')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid mixed values in multiple backgrounds', () => {
|
||||
expect(processBackgroundPosition('left top, invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle negative px values', () => {
|
||||
expect(processBackgroundPosition('-100px')).toEqual([
|
||||
{top: '50%', left: -100},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle negative percentage values', () => {
|
||||
expect(processBackgroundPosition('-25%')).toEqual([
|
||||
{top: '50%', left: '-25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle zero px values', () => {
|
||||
expect(processBackgroundPosition('0px')).toEqual([{top: '50%', left: 0}]);
|
||||
});
|
||||
|
||||
it('should handle zero percentage values', () => {
|
||||
expect(processBackgroundPosition('0%')).toEqual([{top: '50%', left: '0%'}]);
|
||||
});
|
||||
|
||||
it('should handle invalid units', () => {
|
||||
expect(processBackgroundPosition('100em')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid percentage format', () => {
|
||||
expect(processBackgroundPosition('50')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle partial invalid values', () => {
|
||||
expect(processBackgroundPosition('left-')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle partial invalid values with valid ones', () => {
|
||||
expect(processBackgroundPosition('left-, top')).toEqual([]);
|
||||
});
|
||||
|
||||
// Test complex scenarios
|
||||
it('should handle complex multiple background scenario', () => {
|
||||
expect(
|
||||
processBackgroundPosition(
|
||||
'left top, center center, right 25% bottom 75%',
|
||||
),
|
||||
).toEqual([
|
||||
{top: '0%', left: '0%'},
|
||||
{top: '50%', left: '50%'},
|
||||
{bottom: '75%', right: '25%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle all valid single keyword combinations', () => {
|
||||
expect(
|
||||
processBackgroundPosition('left, center, right, top, bottom'),
|
||||
).toEqual([
|
||||
{top: '50%', left: '0%'},
|
||||
{top: '50%', left: '50%'},
|
||||
{top: '50%', left: '100%'},
|
||||
{top: '0%', left: '50%'},
|
||||
{top: '100%', left: '50%'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test specific edge cases
|
||||
it('should handle single space character', () => {
|
||||
expect(processBackgroundPosition(' ')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle multiple space characters', () => {
|
||||
expect(processBackgroundPosition(' ')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle comma with spaces', () => {
|
||||
expect(processBackgroundPosition(' , ')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid three value syntax', () => {
|
||||
expect(processBackgroundPosition('left top invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid four value syntax', () => {
|
||||
expect(processBackgroundPosition('left 25% top invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle 0 as a valid position', () => {
|
||||
expect(processBackgroundPosition('0 0')).toEqual([{top: 0, left: 0}]);
|
||||
});
|
||||
});
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import processBackgroundRepeat from '../processBackgroundRepeat';
|
||||
|
||||
describe('processBackgroundRepeat', () => {
|
||||
// Test null/undefined input
|
||||
it('should handle null input', () => {
|
||||
expect(processBackgroundRepeat(null)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle undefined input', () => {
|
||||
expect(processBackgroundRepeat(undefined)).toEqual([]);
|
||||
});
|
||||
|
||||
// Test empty string input
|
||||
it('should handle empty string', () => {
|
||||
expect(processBackgroundRepeat('')).toEqual([]);
|
||||
});
|
||||
|
||||
// Test array input
|
||||
it('should handle array input', () => {
|
||||
expect(processBackgroundRepeat([{x: 'repeat', y: 'space'}])).toEqual([
|
||||
{x: 'repeat', y: 'space'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test single keyword values
|
||||
it('should parse repeat', () => {
|
||||
expect(processBackgroundRepeat('repeat')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse no-repeat', () => {
|
||||
expect(processBackgroundRepeat('no-repeat')).toEqual([
|
||||
{x: 'no-repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse space', () => {
|
||||
expect(processBackgroundRepeat('space')).toEqual([
|
||||
{x: 'space', y: 'space'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse round', () => {
|
||||
expect(processBackgroundRepeat('round')).toEqual([
|
||||
{x: 'round', y: 'round'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse repeat-x', () => {
|
||||
expect(processBackgroundRepeat('repeat-x')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse repeat-y', () => {
|
||||
expect(processBackgroundRepeat('repeat-y')).toEqual([
|
||||
{x: 'no-repeat', y: 'repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test two-value syntax
|
||||
it('should parse two repeat values', () => {
|
||||
expect(processBackgroundRepeat('repeat repeat')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse repeat no-repeat', () => {
|
||||
expect(processBackgroundRepeat('repeat no-repeat')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse round space', () => {
|
||||
expect(processBackgroundRepeat('round space')).toEqual([
|
||||
{x: 'round', y: 'space'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test multiple background repeats (comma-separated)
|
||||
it('should parse multiple background repeats', () => {
|
||||
expect(processBackgroundRepeat('repeat, no-repeat')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
{x: 'no-repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background repeats with two values', () => {
|
||||
expect(processBackgroundRepeat('repeat no-repeat, space round')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
{x: 'space', y: 'round'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background repeats with mixed syntax', () => {
|
||||
expect(processBackgroundRepeat('repeat-x, space round, no-repeat')).toEqual(
|
||||
[
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
{x: 'space', y: 'round'},
|
||||
{x: 'no-repeat', y: 'no-repeat'},
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse multiple background repeats with newlines', () => {
|
||||
expect(processBackgroundRepeat('repeat,\nno-repeat')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
{x: 'no-repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background repeats with extra whitespace', () => {
|
||||
expect(processBackgroundRepeat(' repeat , no-repeat ')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
{x: 'no-repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test case sensitivity
|
||||
it('should handle mixed case keywords', () => {
|
||||
expect(processBackgroundRepeat('REPEAT')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed case keywords with spaces', () => {
|
||||
expect(processBackgroundRepeat(' Repeat ')).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed case in two values', () => {
|
||||
expect(processBackgroundRepeat('REPEAT NO-REPEAT')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed case in repeat-x', () => {
|
||||
expect(processBackgroundRepeat('REPEAT-X')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test whitespace handling
|
||||
it('should handle extra spaces between values', () => {
|
||||
expect(processBackgroundRepeat('repeat no-repeat')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle tabs and newlines', () => {
|
||||
expect(processBackgroundRepeat('repeat\tno-repeat\n')).toEqual([
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test edge cases and invalid inputs
|
||||
it('should handle invalid single value', () => {
|
||||
expect(processBackgroundRepeat('invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid two values', () => {
|
||||
expect(processBackgroundRepeat('repeat invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle too many values', () => {
|
||||
expect(processBackgroundRepeat('repeat no-repeat space')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty values in comma-separated list', () => {
|
||||
expect(processBackgroundRepeat('repeat,')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle only comma', () => {
|
||||
expect(processBackgroundRepeat(',')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle multiple commas', () => {
|
||||
expect(processBackgroundRepeat('repeat,,no-repeat')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid mixed values in multiple backgrounds', () => {
|
||||
expect(processBackgroundRepeat('repeat, invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid mixed values in multiple backgrounds with two values', () => {
|
||||
expect(processBackgroundRepeat('repeat no-repeat, invalid round')).toEqual(
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
// Test complex scenarios
|
||||
it('should handle all valid two value combinations', () => {
|
||||
expect(
|
||||
processBackgroundRepeat(
|
||||
'repeat repeat, repeat no-repeat, repeat space, repeat round, no-repeat repeat, no-repeat no-repeat, no-repeat space, no-repeat round, space repeat, space no-repeat, space space, space round, round repeat, round no-repeat, round space, round round',
|
||||
),
|
||||
).toEqual([
|
||||
{x: 'repeat', y: 'repeat'},
|
||||
{x: 'repeat', y: 'no-repeat'},
|
||||
{x: 'repeat', y: 'space'},
|
||||
{x: 'repeat', y: 'round'},
|
||||
{x: 'no-repeat', y: 'repeat'},
|
||||
{x: 'no-repeat', y: 'no-repeat'},
|
||||
{x: 'no-repeat', y: 'space'},
|
||||
{x: 'no-repeat', y: 'round'},
|
||||
{x: 'space', y: 'repeat'},
|
||||
{x: 'space', y: 'no-repeat'},
|
||||
{x: 'space', y: 'space'},
|
||||
{x: 'space', y: 'round'},
|
||||
{x: 'round', y: 'repeat'},
|
||||
{x: 'round', y: 'no-repeat'},
|
||||
{x: 'round', y: 'space'},
|
||||
{x: 'round', y: 'round'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test specific edge cases
|
||||
it('should handle single space character', () => {
|
||||
expect(processBackgroundRepeat(' ')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle multiple space characters', () => {
|
||||
expect(processBackgroundRepeat(' ')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle comma with spaces', () => {
|
||||
expect(processBackgroundRepeat(' , ')).toEqual([]);
|
||||
});
|
||||
});
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import processBackgroundSize from '../processBackgroundSize';
|
||||
|
||||
describe('processBackgroundSize', () => {
|
||||
// Test null/undefined input
|
||||
it('should handle null input', () => {
|
||||
expect(processBackgroundSize(null)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle undefined input', () => {
|
||||
expect(processBackgroundSize(undefined)).toEqual([]);
|
||||
});
|
||||
|
||||
// Test empty string input
|
||||
it('should handle empty string', () => {
|
||||
expect(processBackgroundSize('')).toEqual([]);
|
||||
});
|
||||
|
||||
// Test array input
|
||||
it('should handle array input', () => {
|
||||
expect(processBackgroundSize([{x: 100, y: 200}])).toEqual([
|
||||
{x: 100, y: 200},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle array with cover', () => {
|
||||
expect(processBackgroundSize(['cover'])).toEqual(['cover']);
|
||||
});
|
||||
|
||||
it('should handle array with contain', () => {
|
||||
expect(processBackgroundSize(['contain'])).toEqual(['contain']);
|
||||
});
|
||||
|
||||
it('should handle array with mixed values', () => {
|
||||
expect(processBackgroundSize([{x: 100, y: 'auto'}, 'cover'])).toEqual([
|
||||
{x: 100, y: 'auto'},
|
||||
'cover',
|
||||
]);
|
||||
});
|
||||
|
||||
// Test string input - single values
|
||||
it('should parse cover', () => {
|
||||
expect(processBackgroundSize('cover')).toEqual(['cover']);
|
||||
});
|
||||
|
||||
it('should parse contain', () => {
|
||||
expect(processBackgroundSize('contain')).toEqual(['contain']);
|
||||
});
|
||||
|
||||
it('should parse single length value', () => {
|
||||
expect(processBackgroundSize('100px')).toEqual([{x: 100, y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should parse single percentage value', () => {
|
||||
expect(processBackgroundSize('50%')).toEqual([{x: '50%', y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should parse single auto value', () => {
|
||||
expect(processBackgroundSize('auto')).toEqual([{x: 'auto', y: 'auto'}]);
|
||||
});
|
||||
|
||||
// Test string input - two values
|
||||
it('should parse two length values', () => {
|
||||
expect(processBackgroundSize('100px 200px')).toEqual([{x: 100, y: 200}]);
|
||||
});
|
||||
|
||||
it('should parse two percentage values', () => {
|
||||
expect(processBackgroundSize('50% 75%')).toEqual([{x: '50%', y: '75%'}]);
|
||||
});
|
||||
|
||||
it('should parse length and percentage', () => {
|
||||
expect(processBackgroundSize('100px 50%')).toEqual([{x: 100, y: '50%'}]);
|
||||
});
|
||||
|
||||
it('should parse percentage and length', () => {
|
||||
expect(processBackgroundSize('50% 200px')).toEqual([{x: '50%', y: 200}]);
|
||||
});
|
||||
|
||||
it('should parse length and auto', () => {
|
||||
expect(processBackgroundSize('100px auto')).toEqual([{x: 100, y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should parse auto and length', () => {
|
||||
expect(processBackgroundSize('auto 200px')).toEqual([{x: 'auto', y: 200}]);
|
||||
});
|
||||
|
||||
it('should parse percentage and auto', () => {
|
||||
expect(processBackgroundSize('50% auto')).toEqual([{x: '50%', y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should parse auto and percentage', () => {
|
||||
expect(processBackgroundSize('auto 75%')).toEqual([{x: 'auto', y: '75%'}]);
|
||||
});
|
||||
|
||||
it('should parse two auto values', () => {
|
||||
expect(processBackgroundSize('auto auto')).toEqual([
|
||||
{x: 'auto', y: 'auto'},
|
||||
]);
|
||||
});
|
||||
|
||||
// Test multiple background sizes (comma-separated)
|
||||
it('should parse multiple background sizes', () => {
|
||||
expect(processBackgroundSize('100px 200px, cover')).toEqual([
|
||||
{x: 100, y: 200},
|
||||
'cover',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background sizes with spaces', () => {
|
||||
expect(processBackgroundSize('100px 200px, 50% 75%')).toEqual([
|
||||
{x: 100, y: 200},
|
||||
{x: '50%', y: '75%'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background sizes with newlines', () => {
|
||||
expect(processBackgroundSize('100px 200px,\nCover')).toEqual([
|
||||
{x: 100, y: 200},
|
||||
'cover',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse multiple background sizes with extra whitespace', () => {
|
||||
expect(processBackgroundSize(' 100px 200px , cover ')).toEqual([
|
||||
{x: 100, y: 200},
|
||||
'cover',
|
||||
]);
|
||||
});
|
||||
|
||||
// Test edge cases and invalid inputs
|
||||
it('should handle negative length values', () => {
|
||||
expect(processBackgroundSize('-100px')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle negative percentage values', () => {
|
||||
expect(processBackgroundSize('-50%')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle zero length values', () => {
|
||||
expect(processBackgroundSize('0px')).toEqual([{x: 0, y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should handle zero percentage values', () => {
|
||||
expect(processBackgroundSize('0%')).toEqual([{x: '0%', y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should handle decimal length values', () => {
|
||||
expect(processBackgroundSize('100.5px')).toEqual([{x: 100.5, y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should handle decimal percentage values', () => {
|
||||
expect(processBackgroundSize('50.5%')).toEqual([{x: '50.5%', y: 'auto'}]);
|
||||
});
|
||||
|
||||
it('should handle invalid units', () => {
|
||||
expect(processBackgroundSize('100em')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid percentage format', () => {
|
||||
expect(processBackgroundSize('50')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle mixed case keywords', () => {
|
||||
expect(processBackgroundSize('COVER')).toEqual(['cover']);
|
||||
});
|
||||
|
||||
it('should handle mixed case keywords with spaces', () => {
|
||||
expect(processBackgroundSize(' Cover ')).toEqual(['cover']);
|
||||
});
|
||||
|
||||
it('should handle invalid mixed values', () => {
|
||||
expect(processBackgroundSize('100px invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle invalid mixed values in multiple backgrounds', () => {
|
||||
expect(processBackgroundSize('100px 200px, invalid')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle too many values', () => {
|
||||
expect(processBackgroundSize('100px 200px 300px')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty values in comma-separated list', () => {
|
||||
expect(processBackgroundSize('100px 200px,')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle only comma', () => {
|
||||
expect(processBackgroundSize(',')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle multiple commas', () => {
|
||||
expect(processBackgroundSize('100px 200px,,cover')).toEqual([]);
|
||||
});
|
||||
|
||||
// Test whitespace handling
|
||||
it('should handle extra spaces between values', () => {
|
||||
expect(processBackgroundSize('100px 200px')).toEqual([{x: 100, y: 200}]);
|
||||
});
|
||||
|
||||
it('should handle tabs and newlines', () => {
|
||||
expect(processBackgroundSize('100px\t200px\n')).toEqual([{x: 100, y: 200}]);
|
||||
});
|
||||
|
||||
// Test complex scenarios
|
||||
it('should handle complex multiple background scenario', () => {
|
||||
expect(
|
||||
processBackgroundSize('100px 200px, 50% auto, cover, contain'),
|
||||
).toEqual([{x: 100, y: 200}, {x: '50%', y: 'auto'}, 'cover', 'contain']);
|
||||
});
|
||||
|
||||
it('should handle all valid single value combinations', () => {
|
||||
expect(processBackgroundSize('100px, 50%, auto, cover, contain')).toEqual([
|
||||
{x: 100, y: 'auto'},
|
||||
{x: '50%', y: 'auto'},
|
||||
{x: 'auto', y: 'auto'},
|
||||
'cover',
|
||||
'contain',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
import type {BackgroundPositionValue} from './StyleSheetTypes';
|
||||
|
||||
export default function processBackgroundPosition(
|
||||
backgroundPosition: ?($ReadOnlyArray<BackgroundPositionValue> | string),
|
||||
): $ReadOnlyArray<BackgroundPositionValue> {
|
||||
let result: $ReadOnlyArray<BackgroundPositionValue> = [];
|
||||
|
||||
if (backgroundPosition == null) {
|
||||
return [];
|
||||
}
|
||||
if (typeof backgroundPosition === 'string') {
|
||||
result = parseBackgroundPositionCSSString(
|
||||
backgroundPosition.replace(/\n/g, ' '),
|
||||
);
|
||||
} else if (Array.isArray(backgroundPosition)) {
|
||||
result = backgroundPosition;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-backgrounds-3/#typedef-bg-position
|
||||
const parseBackgroundPositionCSSString = (
|
||||
backgroundPosition: string,
|
||||
): $ReadOnlyArray<BackgroundPositionValue> => {
|
||||
const result: Array<BackgroundPositionValue> = [];
|
||||
const positions = backgroundPosition.split(',').map(s => s.trim());
|
||||
|
||||
for (const position of positions) {
|
||||
let top: string | number;
|
||||
let left: string | number;
|
||||
let right: string | number;
|
||||
let bottom: string | number;
|
||||
const parts = position.split(/\s+/).filter(p => p.length > 0);
|
||||
// 1. Single value syntax [ left | center | right | top | bottom | <length-percentage> ]
|
||||
if (parts.length === 1) {
|
||||
const t1 = parts[0];
|
||||
if (t1 == null) {
|
||||
return [];
|
||||
}
|
||||
const token1 = t1.toLowerCase().trim();
|
||||
if (token1 === 'left') {
|
||||
left = '0%';
|
||||
top = '50%';
|
||||
} else if (token1 === 'center') {
|
||||
left = '50%';
|
||||
top = '50%';
|
||||
} else if (token1 === 'right') {
|
||||
left = '100%';
|
||||
top = '50%';
|
||||
} else if (token1 === 'top') {
|
||||
left = '50%';
|
||||
top = '0%';
|
||||
} else if (token1 === 'bottom') {
|
||||
left = '50%';
|
||||
top = '100%';
|
||||
} else if (isValidPosition(token1)) {
|
||||
const value = getPositionFromCSSValue(token1);
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
left = value;
|
||||
top = '50%';
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Two value syntax [ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ]
|
||||
if (parts.length === 2) {
|
||||
const t1 = parts[0];
|
||||
const t2 = parts[1];
|
||||
if (t1 == null || t2 == null) {
|
||||
return [];
|
||||
}
|
||||
const token1 = t1.toLowerCase().trim();
|
||||
if (token1 === 'left') {
|
||||
left = '0%';
|
||||
} else if (token1 === 'center') {
|
||||
left = '50%';
|
||||
} else if (token1 === 'right') {
|
||||
left = '100%';
|
||||
} else if (token1 === 'top') {
|
||||
top = '0%';
|
||||
} else if (token1 === 'bottom') {
|
||||
top = '100%';
|
||||
} else if (isValidPosition(token1)) {
|
||||
const value = getPositionFromCSSValue(token1);
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
left = value;
|
||||
}
|
||||
|
||||
const token2 = t2.toLowerCase().trim();
|
||||
if (token2 === 'top') {
|
||||
top = '0%';
|
||||
} else if (token2 === 'center') {
|
||||
top = '50%';
|
||||
} else if (token2 === 'bottom') {
|
||||
top = '100%';
|
||||
} else if (token2 === 'left') {
|
||||
left = '0%';
|
||||
} else if (token2 === 'right') {
|
||||
left = '100%';
|
||||
} else if (isValidPosition(token2)) {
|
||||
const value = getPositionFromCSSValue(token2);
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
top = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Three value syntax [ center | [ left | right ] <length-percentage>? ] && [ center | [ top | bottom ] <length-percentage>? ]
|
||||
if (parts.length === 3) {
|
||||
const t1 = parts[0];
|
||||
const t2 = parts[1];
|
||||
const t3 = parts[2];
|
||||
if (t1 == null || t2 == null || t3 == null) {
|
||||
return [];
|
||||
}
|
||||
const token1 = t1.toLowerCase().trim();
|
||||
const token2 = t2.toLowerCase().trim();
|
||||
const token3 = t3.toLowerCase().trim();
|
||||
// e.g. center top 40%
|
||||
if (token1 === 'center') {
|
||||
left = '50%';
|
||||
const value = getPositionFromCSSValue(token3);
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
if (token2 === 'top') {
|
||||
top = value;
|
||||
} else if (token2 === 'bottom') {
|
||||
bottom = value;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
// e.g. left 40% center
|
||||
else if (token3 === 'center') {
|
||||
top = '50%';
|
||||
const value = getPositionFromCSSValue(token2);
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
if (token1 === 'left') {
|
||||
left = value;
|
||||
} else if (token1 === 'right') {
|
||||
right = value;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
// e.g. left 40% top, left top 10%
|
||||
else {
|
||||
const tokens = [token1, token2, token3];
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i];
|
||||
if (isValidPosition(token)) {
|
||||
const value = getPositionFromCSSValue(token);
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
const previousToken = tokens[i - 1];
|
||||
if (previousToken === 'left') {
|
||||
left = value;
|
||||
} else if (previousToken === 'right') {
|
||||
right = value;
|
||||
} else if (previousToken === 'top') {
|
||||
top = value;
|
||||
} else if (previousToken === 'bottom') {
|
||||
bottom = value;
|
||||
}
|
||||
} else {
|
||||
if (token === 'left') {
|
||||
left = '0%';
|
||||
} else if (token === 'right') {
|
||||
right = '0%';
|
||||
} else if (token === 'top') {
|
||||
top = '0%';
|
||||
} else if (token === 'bottom') {
|
||||
bottom = '0%';
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Four value syntax [ center | [ left | right ] <length-percentage>? ] && [ center | [ top | bottom ] <length-percentage>? ]
|
||||
if (parts.length === 4) {
|
||||
const t1 = parts.shift();
|
||||
const t2 = parts.shift();
|
||||
const t3 = parts.shift();
|
||||
const t4 = parts.shift();
|
||||
if (t1 == null || t2 == null || t3 == null || t4 == null) {
|
||||
return [];
|
||||
}
|
||||
const token1 = t1.toLowerCase().trim();
|
||||
const token2 = t2.toLowerCase().trim();
|
||||
const token3 = t3.toLowerCase().trim();
|
||||
const token4 = t4.toLowerCase().trim();
|
||||
const keyword1 = token1;
|
||||
const value1 = getPositionFromCSSValue(token2);
|
||||
const keyword2 = token3;
|
||||
const value2 = getPositionFromCSSValue(token4);
|
||||
if (value1 == null || value2 == null) {
|
||||
return [];
|
||||
}
|
||||
if (keyword1 === 'left') {
|
||||
left = value1;
|
||||
} else if (keyword1 === 'right') {
|
||||
right = value1;
|
||||
}
|
||||
|
||||
if (keyword2 === 'top') {
|
||||
top = value2;
|
||||
} else if (keyword2 === 'bottom') {
|
||||
bottom = value2;
|
||||
}
|
||||
}
|
||||
|
||||
if (top != null && left != null) {
|
||||
result.push({
|
||||
top,
|
||||
left,
|
||||
});
|
||||
} else if (bottom != null && right != null) {
|
||||
result.push({
|
||||
bottom,
|
||||
right,
|
||||
});
|
||||
} else if (top != null && right != null) {
|
||||
result.push({
|
||||
top,
|
||||
right,
|
||||
});
|
||||
} else if (bottom != null && left != null) {
|
||||
result.push({
|
||||
bottom,
|
||||
left,
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
function getPositionFromCSSValue(position: string) {
|
||||
if (position.endsWith('px')) {
|
||||
return parseFloat(position);
|
||||
}
|
||||
|
||||
if (position.endsWith('%')) {
|
||||
return position;
|
||||
}
|
||||
|
||||
// CSS length allows 0 as a valid value
|
||||
if (position === '0') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function isValidPosition(position: string) {
|
||||
if (position.endsWith('px') || position.endsWith('%') || position === '0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {
|
||||
BackgroundRepeatKeyword,
|
||||
BackgroundRepeatValue,
|
||||
} from './StyleSheetTypes';
|
||||
|
||||
function isBackgroundRepeatKeyword(
|
||||
value: string,
|
||||
): value is BackgroundRepeatKeyword {
|
||||
return (
|
||||
value === 'repeat' ||
|
||||
value === 'space' ||
|
||||
value === 'round' ||
|
||||
value === 'no-repeat'
|
||||
);
|
||||
}
|
||||
|
||||
export default function processBackgroundRepeat(
|
||||
backgroundRepeat: ?($ReadOnlyArray<BackgroundRepeatValue> | string),
|
||||
): $ReadOnlyArray<BackgroundRepeatValue> {
|
||||
let result: $ReadOnlyArray<BackgroundRepeatValue> = [];
|
||||
if (backgroundRepeat == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(backgroundRepeat)) {
|
||||
return backgroundRepeat;
|
||||
}
|
||||
|
||||
if (typeof backgroundRepeat === 'string') {
|
||||
result = parseBackgroundRepeatCSSString(
|
||||
backgroundRepeat.replace(/\n/g, ' '),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-backgrounds-3/#typedef-repeat-style
|
||||
function parseBackgroundRepeatCSSString(
|
||||
backgroundRepeat: string,
|
||||
): $ReadOnlyArray<BackgroundRepeatValue> {
|
||||
const result: Array<BackgroundRepeatValue> = [];
|
||||
const bgRepeatArray = backgroundRepeat.split(',').map(s => s.trim());
|
||||
|
||||
for (const bgRepeat of bgRepeatArray) {
|
||||
if (bgRepeat.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parts = bgRepeat.split(/\s+/).filter(p => p.length > 0);
|
||||
if (parts.length === 1) {
|
||||
const part1 = parts[0];
|
||||
if (part1 == null) {
|
||||
return [];
|
||||
}
|
||||
const token1 = part1.toLowerCase();
|
||||
if (token1 === 'repeat-x') {
|
||||
result.push({x: 'repeat', y: 'no-repeat'});
|
||||
} else if (token1 === 'repeat-y') {
|
||||
result.push({x: 'no-repeat', y: 'repeat'});
|
||||
} else if (token1 === 'repeat') {
|
||||
result.push({x: 'repeat', y: 'repeat'});
|
||||
} else if (token1 === 'space') {
|
||||
result.push({x: 'space', y: 'space'});
|
||||
} else if (token1 === 'round') {
|
||||
result.push({x: 'round', y: 'round'});
|
||||
} else if (token1 === 'no-repeat') {
|
||||
result.push({x: 'no-repeat', y: 'no-repeat'});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else if (parts.length === 2) {
|
||||
const part1 = parts[0];
|
||||
const part2 = parts[1];
|
||||
if (part1 == null || part2 == null) {
|
||||
return [];
|
||||
}
|
||||
const token1 = part1.toLowerCase();
|
||||
const token2 = part2.toLowerCase();
|
||||
|
||||
if (
|
||||
isBackgroundRepeatKeyword(token1) &&
|
||||
isBackgroundRepeatKeyword(token2)
|
||||
) {
|
||||
result.push({x: token1, y: token2});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {BackgroundSizeValue} from './StyleSheetTypes';
|
||||
|
||||
export default function processBackgroundSize(
|
||||
backgroundSize: ?($ReadOnlyArray<BackgroundSizeValue> | string),
|
||||
): $ReadOnlyArray<BackgroundSizeValue> {
|
||||
let result: $ReadOnlyArray<BackgroundSizeValue> = [];
|
||||
|
||||
if (backgroundSize == null) {
|
||||
// If the size is invalid, return an empty array and do not apply any background size. Same as web.
|
||||
return [];
|
||||
}
|
||||
|
||||
if (typeof backgroundSize === 'string') {
|
||||
result = parseBackgroundSizeCSSString(backgroundSize.replace(/\n/g, ' '));
|
||||
} else if (Array.isArray(backgroundSize)) {
|
||||
result = backgroundSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// https://www.w3.org/TR/css-backgrounds-3/#typedef-bg-size
|
||||
// <bg-size> = [ <length-percentage [0,∞]> | auto ]{1,2} | cover | contain
|
||||
function parseBackgroundSizeCSSString(
|
||||
backgroundSize: string,
|
||||
): $ReadOnlyArray<BackgroundSizeValue> {
|
||||
const result: Array<BackgroundSizeValue> = [];
|
||||
const sizes = backgroundSize.split(',').map(s => s.trim());
|
||||
|
||||
for (const size of sizes) {
|
||||
if (size.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parts = size.split(/\s+/).filter(p => p.length > 0);
|
||||
|
||||
if (parts.length === 2) {
|
||||
const x = getValidLengthPercentageSizeOrNull(parts[0].toLowerCase());
|
||||
const y = getValidLengthPercentageSizeOrNull(parts[1].toLowerCase());
|
||||
if (x != null && y != null) {
|
||||
result.push({
|
||||
x,
|
||||
y,
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else if (parts.length === 1) {
|
||||
const part = parts[0].toLowerCase();
|
||||
if (part === 'cover' || part === 'contain') {
|
||||
result.push(part);
|
||||
} else {
|
||||
const x = getValidLengthPercentageSizeOrNull(parts[0].toLowerCase());
|
||||
if (x != null) {
|
||||
result.push({
|
||||
x,
|
||||
y: 'auto',
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// [ <length-percentage [0,∞]> | auto ]
|
||||
function getValidLengthPercentageSizeOrNull(size: ?string) {
|
||||
if (size == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (size.endsWith('px')) {
|
||||
const num = parseFloat(size);
|
||||
if (!Number.isNaN(num) && num >= 0) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
if (size.endsWith('%')) {
|
||||
if (parseFloat(size) >= 0) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
if (size === 'auto') {
|
||||
return size;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -443,15 +443,14 @@ static YGSize RCTTextShadowViewMeasure(
|
||||
size.width -= letterSpacing;
|
||||
}
|
||||
|
||||
size = (CGSize){
|
||||
MIN(RCTCeilPixelValue(size.width), maximumSize.width), MIN(RCTCeilPixelValue(size.height), maximumSize.height)};
|
||||
size = (CGSize){MIN(RCTCeilPixelValue(size.width), maximumSize.width),
|
||||
MIN(RCTCeilPixelValue(size.height), maximumSize.height)};
|
||||
|
||||
// Adding epsilon value illuminates problems with converting values from
|
||||
// `double` to `float`, and then rounding them to pixel grid in Yoga.
|
||||
CGFloat epsilon = 0.001;
|
||||
return (YGSize){
|
||||
RCTYogaFloatFromCoreGraphicsFloat(size.width + epsilon),
|
||||
RCTYogaFloatFromCoreGraphicsFloat(size.height + epsilon)};
|
||||
return (YGSize){RCTYogaFloatFromCoreGraphicsFloat(size.width + epsilon),
|
||||
RCTYogaFloatFromCoreGraphicsFloat(size.height + epsilon)};
|
||||
}
|
||||
|
||||
static float RCTTextShadowViewBaseline(YGNodeConstRef node, const float width, const float height)
|
||||
|
||||
@@ -285,9 +285,8 @@
|
||||
[_layoutManager ensureLayoutForTextContainer:_textContainer];
|
||||
CGSize size = [_layoutManager usedRectForTextContainer:_textContainer].size;
|
||||
|
||||
return (CGSize){
|
||||
MAX(minimumSize.width, MIN(RCTCeilPixelValue(size.width), maximumSize.width)),
|
||||
MAX(minimumSize.height, MIN(RCTCeilPixelValue(size.height), maximumSize.height))};
|
||||
return (CGSize){MAX(minimumSize.width, MIN(RCTCeilPixelValue(size.width), maximumSize.width)),
|
||||
MAX(minimumSize.height, MIN(RCTCeilPixelValue(size.height), maximumSize.height))};
|
||||
}
|
||||
|
||||
- (CGFloat)lastBaselineForSize:(CGSize)size
|
||||
@@ -348,8 +347,8 @@ static YGSize RCTBaseTextInputShadowViewMeasure(
|
||||
|
||||
CGSize measuredSize = [shadowView sizeThatFitsMinimumSize:minimumSize maximumSize:maximumSize];
|
||||
|
||||
return (YGSize){
|
||||
RCTYogaFloatFromCoreGraphicsFloat(measuredSize.width), RCTYogaFloatFromCoreGraphicsFloat(measuredSize.height)};
|
||||
return (YGSize){RCTYogaFloatFromCoreGraphicsFloat(measuredSize.width),
|
||||
RCTYogaFloatFromCoreGraphicsFloat(measuredSize.height)};
|
||||
}
|
||||
|
||||
static float RCTTextInputShadowViewBaseline(YGNodeConstRef node, const float width, const float height)
|
||||
|
||||
@@ -125,12 +125,9 @@ RCT_EXPORT_METHOD(blur : (nonnull NSNumber *)viewTag)
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setTextAndSelection
|
||||
: (nonnull NSNumber *)viewTag mostRecentEventCount
|
||||
: (NSInteger)mostRecentEventCount value
|
||||
: (NSString *)value start
|
||||
: (NSInteger)start end
|
||||
: (NSInteger)end)
|
||||
RCT_EXPORT_METHOD(
|
||||
setTextAndSelection : (nonnull NSNumber *)viewTag mostRecentEventCount : (NSInteger)
|
||||
mostRecentEventCount value : (NSString *)value start : (NSInteger)start end : (NSInteger)end)
|
||||
{
|
||||
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
RCTBaseTextInputView *view = (RCTBaseTextInputView *)viewRegistry[viewTag];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user