Compare commits

..
Author SHA1 Message Date
CodemodService Bot 80dda2377f Fix CQS signal modernize-use-designated-initializers in xplat/yoga/yoga/algorithm (#54197)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/54197

Reviewed By: christophpurrer

Differential Revision: D84892945
2025-10-21 14:35:29 -07:00
1040 changed files with 17293 additions and 19177 deletions
-3
View File
@@ -35,9 +35,6 @@ 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
View File
@@ -98,4 +98,4 @@ untyped-import
untyped-type-import
[version]
^0.289.0
^0.288.0
@@ -0,0 +1,103 @@
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 }}
@@ -0,0 +1,227 @@
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
@@ -0,0 +1,39 @@
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
@@ -0,0 +1,53 @@
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
@@ -0,0 +1,86 @@
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,6 +4,9 @@ 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
@@ -17,6 +20,87 @@ 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:
@@ -0,0 +1,99 @@
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
@@ -0,0 +1,16 @@
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/.
-22
View File
@@ -5,10 +5,6 @@ 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:
@@ -16,21 +12,3 @@ 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
+31 -8
View File
@@ -10,27 +10,36 @@ 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:
@@ -57,6 +66,20 @@ 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"
+54 -9
View File
@@ -10,9 +10,18 @@ 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
@@ -23,25 +32,60 @@ 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:
@@ -61,6 +105,7 @@ 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"
@@ -71,7 +116,7 @@ runs:
cd packages/rn-tester
bundle install
bundle exec pod update hermes-engine --no-repo-update
bundle exec pod install
- name: Build RNTester
shell: bash
run: |
@@ -121,7 +121,7 @@ describe('Create Draft Release', () => {
});
describe('#_computeBody', () => {
it('computes body for release when no hermes versions are passed', async () => {
it('computes body for release', async () => {
const version = '0.77.1';
const changelog = `## v${version}
### Breaking Changes
@@ -134,83 +134,20 @@ 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(changelog, version);
const body = _computeBody(version, changelog);
expect(body).toEqual(`${changelog}
---
Hermes 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)
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)
- [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)
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).
+5 -21
View File
@@ -40,29 +40,19 @@ function _extractChangelog(version) {
return changelog.slice(changelogStarts, changelogEnds).join('\n').trim();
}
function _computeBody(changelog, version, hermesVersion, hermesV1Version) {
hermesVersion = hermesVersion ?? version;
hermesV1Version = hermesV1Version ?? version;
function _computeBody(version, changelog) {
return `${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)
- [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)
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).
@@ -123,13 +113,7 @@ function moveToChangelogBranch(version) {
run(`git checkout -b changelog/v${version}`);
}
async function createDraftRelease(
version,
latest,
token,
hermesVersion,
hermesV1Version,
) {
async function createDraftRelease(version, latest, token) {
if (version.startsWith('v')) {
version = version.substring(1);
}
@@ -137,7 +121,7 @@ async function createDraftRelease(
_verifyTagExists(version);
moveToChangelogBranch(version);
const changelog = _extractChangelog(version);
const body = _computeBody(changelog, version, hermesVersion, hermesV1Version);
const body = _computeBody(version, changelog);
const release = await _createDraftReleaseOnGitHub(
version,
body,
+1 -1
View File
@@ -162,7 +162,7 @@ async function main() {
console.info(`WORKING_DIRECTORY: ${WORKING_DIRECTORY}`);
console.info('==============================\n');
const simulatorName = 'iPhone 16 Pro';
const simulatorName = 'iPhone 15 Pro';
launchSimulator(simulatorName);
installAppOnSimulator(APP_PATH);
const udid = extractSimulatorUDID();
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Setup xcode
uses: ./.github/actions/setup-xcode
with:
platform: 'ios'
xcode-version: '16.2.0'
- name: Extract branch name
run: |
TAG="${{ github.ref_name }}";
+1 -10
View File
@@ -2,15 +2,6 @@ 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:
@@ -36,7 +27,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}}', ${{ inputs.hermesVersion }}, ${{ inputs.hermesV1Version }})).id;
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}')).id;
result-encoding: string
- name: Upload release assets for DotSlash
uses: actions/github-script@v6
+121 -3
View File
@@ -21,6 +21,86 @@ 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
@@ -28,10 +108,42 @@ jobs:
prebuild_react_native_core:
uses: ./.github/workflows/prebuild-ios-core.yml
with:
use-hermes-nightly: true
secrets: inherit
needs: [prebuild_apple_dependencies]
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 }}
build_android:
runs-on: 8-core-ubuntu
@@ -60,6 +172,10 @@ 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,
@@ -72,6 +188,7 @@ 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 }}
@@ -83,6 +200,7 @@ 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 }}
+30 -16
View File
@@ -2,16 +2,11 @@ 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-15
runs-on: macos-14
strategy:
fail-fast: false
matrix:
@@ -37,20 +32,39 @@ jobs:
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
uses: ./.github/actions/setup-xcode
with:
platform: ${{ matrix.slice }}
xcode-version: '16.2.0'
- name: Yarn Install
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
- name: Set Hermes version
- 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'
shell: bash
run: |
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)
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
fi
echo "Using Hermes version: $HERMES_VERSION"
echo "HERMES_VERSION=$HERMES_VERSION" >> $GITHUB_ENV
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
- name: Download ReactNativeDependencies
uses: actions/download-artifact@v4
with:
@@ -109,7 +123,7 @@ jobs:
packages/react-native/.build/headers
compose-xcframework:
runs-on: macos-15
runs-on: macos-14
needs: [build-rn-slice]
strategy:
fail-fast: false
+21 -15
View File
@@ -7,7 +7,7 @@ on:
jobs:
prepare_workspace:
name: Prepare workspace
runs-on: macos-15
runs-on: macos-14
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: v3-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
key: v2-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: v3-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
key: v2-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-15
runs-on: macos-14
needs: [prepare_workspace]
strategy:
fail-fast: false
@@ -65,17 +65,16 @@ 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: 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 }}
key: v2-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
- name: Yarn Install
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
@@ -86,8 +85,15 @@ 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 }}
@@ -101,14 +107,14 @@ jobs:
uses: actions/cache/save@v4
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
with:
key: v3-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
key: v2-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-15
runs-on: macos-14
needs: [build-apple-slices]
strategy:
fail-fast: false
@@ -132,7 +138,7 @@ jobs:
with:
path: |
packages/react-native/third-party/
key: v3-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
key: v2-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'
@@ -194,4 +200,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: v3-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
key: v2-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
+113 -19
View File
@@ -19,27 +19,84 @@ jobs:
echo "Setting release type to release"
echo "RELEASE_TYPE=release" >> $GITHUB_OUTPUT
set_hermes_versions:
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:
HERMES_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}
HERMES_V1_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}
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
- 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)
- 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 }}
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 }}"
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 }}
prebuild_apple_dependencies:
if: github.repository == 'facebook/react-native'
@@ -49,13 +106,51 @@ jobs:
prebuild_react_native_core:
uses: ./.github/workflows/prebuild-ios-core.yml
secrets: inherit
needs: [prebuild_apple_dependencies]
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 }}
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,
]
@@ -67,6 +162,7 @@ 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 }}
@@ -82,6 +178,7 @@ 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 }}
@@ -139,9 +236,6 @@ jobs:
secrets: inherit
create_draft_release:
needs: [generate_changelog, set_hermes_versions]
needs: generate_changelog
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 }}
+163 -24
View File
@@ -33,6 +33,86 @@ 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
@@ -40,15 +120,16 @@ 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]
needs: [prebuild_apple_dependencies, build_hermes_macos]
test_ios_rntester_ruby_3_2_0:
runs-on: macos-15
runs-on: macos-14
needs:
[prebuild_apple_dependencies, prebuild_react_native_core]
[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
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -56,12 +137,17 @@ 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-15-large
runs-on: macos-14-large
needs:
[prebuild_apple_dependencies, prebuild_react_native_core]
[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
continue-on-error: true
strategy:
fail-fast: false
@@ -75,12 +161,17 @@ 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-15-large
runs-on: macos-14-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:
@@ -99,8 +190,6 @@ 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:
@@ -110,8 +199,11 @@ jobs:
flavor: ${{ matrix.flavor }}
test_e2e_ios_templateapp:
runs-on: macos-15-large
runs-on: macos-14-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:
@@ -121,8 +213,6 @@ 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
@@ -131,6 +221,11 @@ 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:
@@ -164,6 +259,9 @@ 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 }}
@@ -180,7 +278,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"
RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
xcodebuild \
-scheme "RNTestProject" \
@@ -266,6 +364,21 @@ 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]
@@ -285,6 +398,25 @@ 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]
@@ -339,6 +471,10 @@ 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,
@@ -348,12 +484,15 @@ 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 }}
@@ -396,12 +535,6 @@ 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)"
@@ -422,10 +555,11 @@ jobs:
compression-level: 0
test_ios_helloworld_with_ruby_3_2_0:
runs-on: macos-15
needs: [prebuild_apple_dependencies, prebuild_react_native_core]
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
env:
PROJECT_NAME: iOSTemplateProject
HERMES_WS_DIR: /tmp/hermes
YARN_ENABLE_IMMUTABLE_INSTALLS: false
steps:
- name: Checkout
@@ -434,10 +568,12 @@ 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-15
needs: [prebuild_apple_dependencies, prebuild_react_native_core]
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
strategy:
matrix:
flavor: [Debug, Release]
@@ -448,6 +584,7 @@ jobs:
use_frameworks: StaticLibraries
env:
PROJECT_NAME: iOSTemplateProject
HERMES_WS_DIR: /tmp/hermes
YARN_ENABLE_IMMUTABLE_INSTALLS: false
steps:
- name: Checkout
@@ -456,6 +593,8 @@ 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
-9
View File
@@ -1,14 +1,5 @@
# 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
-4
View File
@@ -824,10 +824,6 @@
- **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)
+6 -1
View File
@@ -104,6 +104,10 @@ 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") {
@@ -131,7 +135,8 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(project(":packages:react-native:ReactAndroid:hermes-engine"))
.using(module("com.facebook.hermes:hermes-android:0.+"))
// TODO: T237406039 update coordinates
.using(module("com.facebook.react:hermes-android:0.+"))
.because("Users opted to use hermes from nightly")
}
}
+2 -1
View File
@@ -86,7 +86,7 @@
"eslint-plugin-relay": "^1.8.3",
"fb-dotslash": "0.5.8",
"flow-api-translator": "0.32.0",
"flow-bin": "^0.289.0",
"flow-bin": "^0.288.0",
"glob": "^7.1.1",
"hermes-eslint": "0.32.0",
"hermes-transform": "0.32.0",
@@ -120,6 +120,7 @@
"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"
+2 -2
View File
@@ -1,5 +1,5 @@
@generated SignedSource<<4bb67766e15e25a481c4c38873260a7a>>
Git revision: 7aa57d13e50ce9d74a91c9315c9b0ded00fbc19f
@generated SignedSource<<ec85a720cb9603510d40915ba1410b50>>
Git revision: 54afd8765db32a9640edef3788085b991fe4b88a
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebook/react-native-devtools-frontend
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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};
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};
@@ -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_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_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};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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": "^7.0.1",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-native": "^4.0.0"
},
"peerDependencies": {
@@ -14,6 +14,7 @@ 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
@@ -48,6 +49,7 @@ 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
@@ -78,6 +80,7 @@ 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)
@@ -63,6 +63,8 @@ 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>
@@ -94,7 +96,8 @@ abstract class BundleHermesCTask : DefaultTask() {
runCommand(bundleCommand)
if (hermesEnabled.get()) {
val detectedHermesCommand = detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get())
val detectedHermesCommand =
detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled.get())
val bytecodeFile = File("${bundleFile}.hbc")
val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
@@ -14,7 +14,6 @@ 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
@@ -140,7 +139,11 @@ 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(
"${coordinates.hermesGroupString}:hermes-android:${if (hermesV1Enabled) coordinates.hermesV1VersionString else coordinates.hermesVersionString}"
// TODO: T237406039 update coordinates
if (hermesV1Enabled)
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
else
"${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
)
}
}
@@ -151,10 +154,13 @@ 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>>()
val hermesVersion =
if (hermesV1Enabled) coordinates.hermesV1VersionString else coordinates.hermesVersionString
val hermesVersionString = "${coordinates.hermesGroupString}:hermes-android:${hermesVersion}"
// TODO: T237406039 update coordinates
val hermesVersionString =
if (hermesV1Enabled)
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
else "${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
dependencySubstitution.add(
Triple(
"com.facebook.react:react-native",
@@ -169,13 +175,6 @@ 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(
@@ -184,6 +183,7 @@ 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,13 +191,12 @@ internal object DependencyUtils {
"The hermes-android dependency was modified to use the correct Maven group.",
)
)
}
if (coordinates.hermesGroupString != DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP) {
} else if (hermesV1Enabled) {
dependencySubstitution.add(
Triple(
"com.facebook.hermes:hermes-android",
"com.facebook.react:hermes-android",
hermesVersionString,
"The hermes-android dependency was modified to use the correct Maven group.",
"The hermes-android dependency was modified to use Hermes V1.",
)
)
}
@@ -222,19 +221,11 @@ 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 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 hermesVersion = versionString
val hermesV1Version =
(hermesVersionProperties[INTERNAL_HERMES_V1_VERSION_NAME] as? String).orEmpty()
@@ -130,6 +130,7 @@ 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 =
@@ -150,9 +151,13 @@ internal fun detectOSAwareHermesCommand(
return builtHermesc.cliPath(projectRoot)
}
// 3. Use hermes-compiler from npm
// 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
val prebuiltHermesPath =
HERMES_COMPILER_NPM_DIR.plus(getHermesCBin())
hermesCPath
.plus(getHermesCBin())
.replace("%OS-BIN%", getHermesOSBin())
// Execution on Windows fails with / as separator
.replace('/', File.separatorChar)
@@ -238,5 +243,6 @@ 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/"
@@ -81,9 +81,8 @@ object PropertyUtils {
const val INTERNAL_VERSION_NAME = "VERSION_NAME"
/**
* Internal properties, shared with iOS, used to control the version name of Hermes Engine. They
* are stored in sdks/hermes-engine/version.properties
* Internal property, shared with iOS, used to control the version name of Hermes Engine. This is
* 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"
}
@@ -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.hermes:hermes-android:4.5.6" })
assertThat(forcedModules.any { it.toString() == "com.facebook.react: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.hermes:hermes-android:4.5.6" })
assertThat(appForcedModules.any { it.toString() == "com.facebook.react: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.hermes:hermes-android:4.5.6" })
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
.isTrue()
}
@@ -381,15 +381,11 @@ 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:hermes-android:4.5.6" }
)
assertThat(appForcedModules.any { it.toString() == "io.github.test: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:hermes-android:4.5.6" }
)
assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
.isTrue()
}
@@ -442,7 +438,7 @@ class DependencyUtilsTest {
)
.isEqualTo(dependencySubstitutions[0].third)
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
assertThat("com.facebook.hermes:hermes-android:0.42.0")
assertThat("com.facebook.react: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."
@@ -494,26 +490,19 @@ class DependencyUtilsTest {
)
.isEqualTo(dependencySubstitutions[0].third)
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
assertThat("io.github.test.hermes:hermes-android:0.42.0")
.isEqualTo(dependencySubstitutions[1].second)
assertThat("io.github.test: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: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:react-android").isEqualTo(dependencySubstitutions[3].first)
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[3].second)
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[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)
.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.")
.isEqualTo(dependencySubstitutions[4].third)
.isEqualTo(dependencySubstitutions[3].third)
}
@Test
@@ -543,20 +532,15 @@ 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:hermes-android").isEqualTo(dependencySubstitutions[2].first)
assertThat("io.github.test.hermes:hermes-android:0.43.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:react-android").isEqualTo(dependencySubstitutions[3].first)
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[3].second)
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[3].third)
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[4].first)
.isEqualTo(dependencySubstitutions[2].third)
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[3].first)
assertThat("io.github.test.hermes:hermes-android:0.43.0")
.isEqualTo(dependencySubstitutions[4].second)
.isEqualTo(dependencySubstitutions[3].second)
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
.isEqualTo(dependencySubstitutions[4].third)
.isEqualTo(dependencySubstitutions[3].third)
}
@Test
@@ -576,7 +560,6 @@ class DependencyUtilsTest {
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_VERSION_NAME=1000.0.0
HERMES_V1_VERSION_NAME=1000.0.0
ANOTHER_PROPERTY=true
"""
@@ -613,7 +596,6 @@ 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
"""
@@ -627,7 +609,7 @@ class DependencyUtilsTest {
val hermesV1VersionString = strings.hermesV1VersionString
assertThat(versionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
assertThat(hermesVersionString).isEqualTo("0.14.0")
assertThat(hermesVersionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
assertThat(hermesV1VersionString).isEqualTo("250829098.0.0-stable")
}
@@ -679,7 +661,6 @@ class DependencyUtilsTest {
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_VERSION_NAME=
HERMES_V1_VERSION_NAME=
ANOTHER_PROPERTY=true
"""
@@ -714,7 +695,6 @@ class DependencyUtilsTest {
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_VERSION_NAME=
HERMES_V1_VERSION_NAME=
ANOTHER_PROPERTY=true
"""
@@ -746,7 +726,6 @@ class DependencyUtilsTest {
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_VERSION_NAME=
HERMES_V1_VERSION_NAME=
ANOTHER_PROPERTY=true
"""
@@ -155,11 +155,21 @@ class PathUtilsTest {
@Test
@WithOs(OS.MAC)
fun detectOSAwareHermesCommand_withHermescFromNPM() {
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() {
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, "")).isEqualTo(expected.toString())
assertThat(detectOSAwareHermesCommand(tempFolder.root, "", hermesV1Enabled = true))
.isEqualTo(expected.toString())
}
@Test(expected = IllegalStateException::class)
+1 -2
View File
@@ -123,8 +123,7 @@ function getHermesLabel(): React.Node {
return (
<ThemedText color="secondary" style={styles.label}>
JS Engine: Hermes (
{global.HermesInternal.getRuntimeProperties?.()['OSS Release Version']})
JS Engine: Hermes
</ThemedText>
);
}
@@ -26,9 +26,7 @@ describe('console.timeStamp()', () => {
it("doesn't throw when additional arguments are specified", () => {
expect(() =>
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error', {
tooltipText: 'Image processing failed',
}),
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error'),
).not.toThrow();
});
@@ -24,9 +24,11 @@ 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, ^{
@@ -54,9 +56,10 @@ RCT_EXPORT_METHOD(
});
}
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, ^{
@@ -99,7 +99,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(new Error('NativeAccessibilityManagerIOS is not available'));
reject(null);
}
});
}
@@ -119,11 +119,7 @@ const AccessibilityInfo = {
if (NativeAccessibilityInfoAndroid?.isGrayscaleEnabled != null) {
NativeAccessibilityInfoAndroid.isGrayscaleEnabled(resolve);
} else {
reject(
new Error(
'NativeAccessibilityInfoAndroid.isGrayscaleEnabled is not available',
),
);
reject(null);
}
});
} else {
@@ -134,7 +130,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(new Error('AccessibilityInfo native module is not available'));
reject(null);
}
});
}
@@ -154,11 +150,7 @@ const AccessibilityInfo = {
if (NativeAccessibilityInfoAndroid?.isInvertColorsEnabled != null) {
NativeAccessibilityInfoAndroid.isInvertColorsEnabled(resolve);
} else {
reject(
new Error(
'NativeAccessibilityInfoAndroid.isInvertColorsEnabled is not available',
),
);
reject(null);
}
});
} else {
@@ -169,7 +161,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(new Error('AccessibilityInfo native module is not available'));
reject(null);
}
});
}
@@ -189,7 +181,7 @@ const AccessibilityInfo = {
if (NativeAccessibilityInfoAndroid != null) {
NativeAccessibilityInfoAndroid.isReduceMotionEnabled(resolve);
} else {
reject(new Error('AccessibilityInfo native module is not available'));
reject(null);
}
} else {
if (NativeAccessibilityManagerIOS != null) {
@@ -198,7 +190,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(new Error('NativeAccessibilityManagerIOS is not available'));
reject(null);
}
}
});
@@ -216,11 +208,7 @@ const AccessibilityInfo = {
if (NativeAccessibilityInfoAndroid?.isHighTextContrastEnabled != null) {
NativeAccessibilityInfoAndroid.isHighTextContrastEnabled(resolve);
} else {
reject(
new Error(
'NativeAccessibilityInfoAndroid.isHighTextContrastEnabled is not available',
),
);
reject(null);
}
} else {
return Promise.resolve(false);
@@ -248,11 +236,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(
new Error(
'NativeAccessibilityManagerIOS.getCurrentDarkerSystemColorsState is not available',
),
);
reject(null);
}
}
});
@@ -280,11 +264,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(
new Error(
'NativeAccessibilityManagerIOS.getCurrentPrefersCrossFadeTransitionsState is not available',
),
);
reject(null);
}
}
});
@@ -309,7 +289,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(new Error('NativeAccessibilityManagerIOS is not available'));
reject(null);
}
});
}
@@ -329,7 +309,7 @@ const AccessibilityInfo = {
if (NativeAccessibilityInfoAndroid != null) {
NativeAccessibilityInfoAndroid.isTouchExplorationEnabled(resolve);
} else {
reject(new Error('NativeAccessibilityInfoAndroid is not available'));
reject(null);
}
} else {
if (NativeAccessibilityManagerIOS != null) {
@@ -338,7 +318,7 @@ const AccessibilityInfo = {
reject,
);
} else {
reject(new Error('NativeAccessibilityManagerIOS is not available'));
reject(null);
}
}
});
@@ -363,18 +343,10 @@ const AccessibilityInfo = {
) {
NativeAccessibilityInfoAndroid.isAccessibilityServiceEnabled(resolve);
} else {
reject(
new Error(
'NativeAccessibilityInfoAndroid.isAccessibilityServiceEnabled is not available',
),
);
reject(null);
}
} else {
reject(
new Error(
'isAccessibilityServiceEnabled is only available on Android',
),
);
reject(null);
}
});
},
@@ -13,9 +13,6 @@ 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';
@@ -147,25 +144,11 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
: {process: processBoxShadow},
/**
* BackgroundImage
* Linear Gradient
*/
experimental_backgroundImage: {process: processBackgroundImage},
/**
* BackgroundSize
*/
experimental_backgroundSize: {process: processBackgroundSize},
/**
* BackgroundPosition
*/
experimental_backgroundPosition: {process: processBackgroundPosition},
/**
* BackgroundRepeat
*/
experimental_backgroundRepeat: {process: processBackgroundRepeat},
experimental_backgroundImage: ReactNativeFeatureFlags.enableNativeCSSParsing()
? true
: {process: processBackgroundImage},
/**
* View
*/
@@ -332,9 +332,7 @@ 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(
new Error('Unexpectedly failed to call measure on an instance.'),
);
reject('Unexpectedly failed to call measure on an instance.');
}
resolve({
@@ -482,11 +480,7 @@ class DebuggingOverlayRegistry {
width == null ||
height == null
) {
reject(
new Error(
'Unexpectedly failed to call measure on an instance.',
),
);
reject('Unexpectedly failed to call measure on an instance.');
}
resolve({x: left, y: top, width, height});
@@ -30,11 +30,8 @@ class RCTDeviceEventEmitterImpl extends EventEmitter<RCTDeviceEventDefinitions>
...args: RCTDeviceEventDefinitions[TEvent]
): void {
beginEvent(() => `RCTDeviceEventEmitter.emit#${eventType}`);
try {
super.emit(eventType, ...args);
} finally {
endEvent();
}
super.emit(eventType, ...args);
endEvent();
}
}
const RCTDeviceEventEmitter: IEventEmitter<RCTDeviceEventDefinitions> =
@@ -26,81 +26,68 @@ 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]);
}
@@ -111,32 +98,29 @@ 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,28 +31,24 @@ 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");
@@ -63,47 +59,41 @@ 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,9 +38,11 @@ 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,8 +1229,10 @@ 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
@@ -1246,9 +1248,11 @@ RCT_EXPORT_METHOD(
}];
}
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];
@@ -1265,15 +1269,20 @@ RCT_EXPORT_METHOD(
}];
}
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
@@ -1297,8 +1306,10 @@ RCT_EXPORT_METHOD(
}];
}
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,9 +106,10 @@ 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) {
@@ -122,9 +123,10 @@ RCT_EXPORT_METHOD(
});
}
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,11 +101,12 @@ 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:
@@ -114,16 +115,17 @@ 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:
@@ -138,11 +140,12 @@ 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,9 +53,10 @@ 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
@@ -68,9 +69,10 @@ RCT_EXPORT_METHOD(
}];
}
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
@@ -83,9 +85,10 @@ RCT_EXPORT_METHOD(
}];
}
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);
@@ -104,8 +107,10 @@ RCT_EXPORT_METHOD(
}];
}
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,8 +87,10 @@ 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:@{}
@@ -112,8 +114,10 @@ RCT_EXPORT_METHOD(
}];
}
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
@@ -177,9 +181,11 @@ 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));
}
+1 -4
View File
@@ -87,7 +87,7 @@ export default function Ansi({
<View style={styles.container}>
{parsedLines.map((items, i) => (
<View style={styles.line} key={i}>
<Text style={styles.text}>
<Text>
{items.map((bundle, key) => {
const textStyle =
bundle.fg && COLORS[bundle.fg]
@@ -122,7 +122,4 @@ const styles = StyleSheet.create({
line: {
flexDirection: 'row',
},
text: {
flexGrow: 1,
},
});
@@ -118,9 +118,11 @@ 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]
@@ -226,9 +228,10 @@ 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());
@@ -244,9 +247,10 @@ RCT_EXPORT_METHOD(
}];
}
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,9 +108,11 @@ 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]
@@ -202,9 +204,10 @@ 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());
@@ -220,9 +223,10 @@ RCT_EXPORT_METHOD(
}];
}
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,15 +182,6 @@ 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},
@@ -23,9 +23,7 @@ export function unstable_hasComponent(name: string): boolean {
hasNativeComponent = global.__nativeComponentRegistry__hasComponent(name);
componentNameToExists.set(name, hasNativeComponent);
} else {
throw new Error(
`unstable_hasComponent('${name}'): Global function is not registered`,
);
throw `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();
+39 -1
View File
@@ -14,6 +14,7 @@ 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';
@@ -33,7 +34,13 @@ 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 =
@@ -134,6 +141,7 @@ class XMLHttpRequest extends EventTarget {
static DONE: number = DONE;
static _interceptor: ?XHRInterceptor = null;
static _profiling: boolean = false;
UNSENT: number = UNSENT;
OPENED: number = OPENED;
@@ -174,6 +182,10 @@ class XMLHttpRequest extends EventTarget {
XMLHttpRequest._interceptor = interceptor;
}
static enableProfiling(enableProfiling: boolean): void {
XMLHttpRequest._profiling = enableProfiling;
}
constructor() {
super();
this._reset();
@@ -377,12 +389,23 @@ 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);
@@ -427,7 +450,16 @@ 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);
@@ -697,6 +729,12 @@ 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,26 +8,15 @@
* @format
*/
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
import Pressability, {
type EventHandlers,
type PressabilityConfig,
} from './Pressability';
import {useEffect, useInsertionEffect, useRef} from 'react';
import {useEffect, 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
@@ -51,7 +40,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.
useConfigurationEffect(() => {
useEffect(() => {
if (config != null && pressability != null) {
pressability.configure(config);
}
@@ -59,7 +48,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.
useConfigurationEffect(() => {
useEffect(() => {
if (pressability != null) {
return () => {
pressability.reset();
@@ -327,9 +327,10 @@ 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(
@@ -505,8 +506,9 @@ 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
@@ -0,0 +1,152 @@
/**
* 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);
}
}
}
@@ -13,62 +13,124 @@
* 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 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';
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
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 createReactNativeDocument(rootTag);
return null;
}
export function createPublicInstance(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: InternalInstanceHandle,
ownerDocument: ReactNativeDocument,
): ReactNativeElement {
return new ReactNativeElement(
tag,
viewConfig,
internalInstanceHandle,
ownerDocument,
);
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,
);
}
}
export function createPublicTextInstance(
internalInstanceHandle: InternalInstanceHandle,
ownerDocument: ReactNativeDocument,
): ReadOnlyText {
ownerDocument: ReactNativeDocumentT,
): ReadOnlyTextT {
const ReadOnlyText = getReadOnlyTextClass();
return new ReadOnlyText(internalInstanceHandle, ownerDocument);
}
export function getNativeTagFromPublicInstance(
publicInstance: ReactNativeElement,
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
): number {
return publicInstance.__nativeTag;
}
export function getNodeFromPublicInstance(
publicInstance: ReactNativeElement,
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
): ?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,
@@ -76,7 +138,7 @@ export function getNodeFromPublicInstance(
}
export function getInternalInstanceHandleFromPublicInstance(
publicInstance: ReactNativeElement,
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
): 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.
@@ -0,0 +1,75 @@
/**
* 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,
};
});
@@ -0,0 +1,353 @@
/**
* 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'} />);
});
});
});
@@ -1,181 +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.
*
* @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,12 +17,6 @@ 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;
@@ -209,12 +203,6 @@ 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':
@@ -7,9 +7,9 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<90e5b8f55761c6a4805052d5b49689b8>>
*
* This file is no longer sync'd from the facebook/react repository.
* The version compatability check is removed. Use at your own risk.
* This file was sync'd from the facebook/react repository.
*/
"use strict";
@@ -19455,6 +19455,13 @@ __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
@@ -7,9 +7,9 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<14f9eca680f3065d18eda114fb4bea9f>>
*
* This file is no longer sync'd from the facebook/react repository.
* The version compatability check is removed. Use at your own risk.
* This file was sync'd from the facebook/react repository.
*/
"use strict";
@@ -10918,6 +10918,13 @@ 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,9 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<238b7d9e5ffe0f30b64b3b5c7c156088>>
*
* This file is no longer sync'd from the facebook/react repository.
* The version compatability check is removed. Use at your own risk.
* This file was sync'd from the facebook/react repository.
*/
"use strict";
@@ -12713,6 +12713,13 @@ 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,7 +7,9 @@
* @noformat
* @nolint
* @flow
* @generated SignedSource<<cf323fc5ca893bab5669c7d321660412>>
* @generated SignedSource<<16b364e89f43b8a47832b0dfb98af11e>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict-local
* @generated SignedSource<<908f5fb85384725318e261f40e49d9a6>>
* @generated SignedSource<<1dd9e9c3f20e37ae14e485fc6ee3d9e9>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,9 +7,9 @@
* @noformat
* @nolint
* @flow
* @generated SignedSource<<e2c46705ed927302dbe9332dafba459d>>
*
* This file is no longer sync'd from the facebook/react repository.
* The version compatability check is removed. Use at your own risk.
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict
* @generated SignedSource<<c0e57723772ea5f1aa8c3c897ac3c216>>
* @generated SignedSource<<989e6e2e860dc2af7ba983849111bda8>>
*
* This file was sync'd from the facebook/react repository.
*/
import type {
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict-local
* @generated SignedSource<<1f7876c0dc0b05685a730513dc410236>>
* @generated SignedSource<<67d18226984338ab9301147ce0a7d414>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict-local
* @generated SignedSource<<52163887de05f1cff05388145cf85b3b>>
* @generated SignedSource<<556d1487de0b9e4a09cbc67dd130a884>>
*
* This file was sync'd from the facebook/react repository.
*/
'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,25 +67,21 @@ 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,43 +779,6 @@ 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,
@@ -890,13 +853,6 @@ 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',
}>;
@@ -1,478 +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
*/
'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}]);
});
});
@@ -1,243 +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
*/
'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([]);
});
});
@@ -1,231 +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
*/
'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',
]);
});
});
@@ -1,284 +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
*/
'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;
}
@@ -1,105 +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
*/
'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;
}
@@ -1,104 +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
*/
'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,14 +443,15 @@ 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,8 +285,9 @@
[_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
@@ -347,8 +348,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,9 +125,12 @@ 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];
@@ -24,9 +24,15 @@ function requireModule<T: TurboModule>(name: string): ?T {
}
}
const legacyModule: ?T = NativeModules[name];
if (legacyModule != null) {
return legacyModule;
if (
global.RN$Bridgeless !== true ||
global.RN$TurboInterop === true ||
global.RN$UnifiedNativeModuleProxy === true
) {
const legacyModule: ?T = NativeModules[name];
if (legacyModule != null) {
return legacyModule;
}
}
return null;
@@ -27,8 +27,10 @@
// See subclassing notes in
// https://developer.apple.com/documentation/foundation/nsdictionary#//apple_ref/occ/cl/NSDictionary
RCT_NOT_IMPLEMENTED(-(instancetype)initWithObjects : (id _Nonnull const[])
objects forKeys : (id<NSCopying> _Nonnull const[])keys count : (NSUInteger)count)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithObjects
: (id _Nonnull const[])objects forKeys
: (id<NSCopying> _Nonnull const[])keys count
: (NSUInteger)count)
- (NSUInteger)count
{
@@ -295,12 +295,10 @@ RCT_EXTERN_C_END
/**
* Like RCT_EXTERN_MODULE, but allows setting a custom JavaScript name.
*/
#define RCT_EXTERN_REMAP_MODULE(js_name, objc_name, objc_supername) \
objc_name: \
objc_supername @ \
end @interface objc_name(RCTExternModule)<RCTBridgeModule> \
@end \
@implementation objc_name (RCTExternModule) \
#define RCT_EXTERN_REMAP_MODULE(js_name, objc_name, objc_supername) \
objc_name : objc_supername @end @interface objc_name(RCTExternModule)<RCTBridgeModule> \
@end \
@implementation objc_name (RCTExternModule) \
RCT_EXPORT_MODULE_NO_LOAD(js_name, objc_name)
/**
@@ -857,7 +857,7 @@ static UIColor *RCTColorFromSemanticColorName(NSString *semanticColorName)
RCTAssert([UIColor respondsToSelector:selector], @"RCTUIColor does not respond to a semantic color selector.");
Class klass = [UIColor class];
IMP imp = [klass methodForSelector:selector];
id (*getSemanticColorObject)(id, SEL) = (id (*)(id, SEL))imp;
id (*getSemanticColorObject)(id, SEL) = (id(*)(id, SEL))imp;
id colorObject = getSemanticColorObject(klass, selector);
if ([colorObject isKindOfClass:[UIColor class]]) {
color = colorObject;
@@ -1073,7 +1073,7 @@ NSArray *RCTConvertArrayValue(SEL type, id json)
__block BOOL copy = NO;
__block NSArray *values = json = [RCTConvert NSArray:json];
[json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
id value = ((id (*)(Class, SEL, id))objc_msgSend)([RCTConvert class], type, jsonValue);
id value = ((id(*)(Class, SEL, id))objc_msgSend)([RCTConvert class], type, jsonValue);
if (copy) {
if (value) {
[(NSMutableArray *)values addObject:value];

Some files were not shown because too many files have changed in this diff Show More