mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
185443e299 |
+7
-1
@@ -75,6 +75,12 @@ module.system.haste.module_ref_prefix=m#
|
||||
|
||||
react.runtime=automatic
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
suppress_type=$FlowFixMeEmpty
|
||||
|
||||
ban_spread_key_props=true
|
||||
|
||||
[lints]
|
||||
@@ -98,4 +104,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.281.0
|
||||
^0.275.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: 🔍 Debugger - Bug Report
|
||||
description: Report a bug with React Native DevTools and the New Debugger
|
||||
labels: ["Needs: Triage :mag:", "Debugging"]
|
||||
labels: ["Needs: Triage :mag:", "Debugger"]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
|
||||
@@ -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
|
||||
run-e2e-tests:
|
||||
default: 'false'
|
||||
description: If we need to build to run E2E tests. If yes, we need to build also x86.
|
||||
gradle-cache-encryption-key:
|
||||
description: "The encryption key needed to store the Gradle Configuration cache"
|
||||
runs:
|
||||
@@ -28,11 +31,10 @@ runs:
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: /github/home/.cache/ccache
|
||||
key: v2-ccache-android-${{ github.job }}-${{ github.ref }}-${{ hashFiles('packages/react-native/ReactAndroid/**/*.cpp', 'packages/react-native/ReactAndroid/**/*.h', 'packages/react-native/ReactCommon/**/*.cpp', 'packages/react-native/ReactAndroid/**/CMakeLists.txt', 'packages/react-native/ReactCommon/**/CMakeLists.txt') }}
|
||||
key: v1-ccache-android-${{ github.job }}-${{ github.ref }}
|
||||
restore-keys: |
|
||||
v2-ccache-android-${{ github.job }}-${{ github.ref }}-
|
||||
v2-ccache-android-${{ github.job }}-
|
||||
v2-ccache-android-
|
||||
v1-ccache-android-${{ github.job }}-
|
||||
v1-ccache-android-
|
||||
- name: Show ccache stats
|
||||
shell: bash
|
||||
run: ccache -s -v
|
||||
@@ -41,7 +43,11 @@ runs:
|
||||
run: |
|
||||
if [[ "${{ inputs.release-type }}" == "dry-run" ]]; then
|
||||
# dry-run: we only build ARM64 to save time/resources. For release/nightlies the default is to build all archs.
|
||||
export ORG_GRADLE_PROJECT_reactNativeArchitectures="arm64-v8a,x86" # x86 is required for E2E testing
|
||||
if [[ "${{ inputs.run-e2e-tests }}" == 'true' ]]; then
|
||||
export ORG_GRADLE_PROJECT_reactNativeArchitectures="arm64-v8a,x86" # x86 is required for E2E testing
|
||||
else
|
||||
export ORG_GRADLE_PROJECT_reactNativeArchitectures="arm64-v8a"
|
||||
fi
|
||||
TASKS="publishAllToMavenTempLocal build"
|
||||
elif [[ "${{ inputs.release-type }}" == "nightly" ]]; then
|
||||
# nightly: we set isSnapshot to true so artifacts are sent to the right repository on Maven Central.
|
||||
@@ -57,7 +63,7 @@ runs:
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: /github/home/.cache/ccache
|
||||
key: v2-ccache-android-${{ github.job }}-${{ github.ref }}-${{ hashFiles('packages/react-native/ReactAndroid/**/*.cpp', 'packages/react-native/ReactAndroid/**/*.h', 'packages/react-native/ReactCommon/**/*.cpp', 'packages/react-native/ReactAndroid/**/CMakeLists.txt', 'packages/react-native/ReactCommon/**/CMakeLists.txt') }}
|
||||
key: v1-ccache-android-${{ github.job }}-${{ github.ref }}
|
||||
- name: Show ccache stats
|
||||
shell: bash
|
||||
run: ccache -s -v
|
||||
|
||||
@@ -71,17 +71,17 @@ runs:
|
||||
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"
|
||||
if [[ -d "$FINAL_PATH/API/hermes/hermes.framework" ]]; then
|
||||
echo "Successfully built hermes.framework for $SLICE in $FLAVOR"
|
||||
else
|
||||
echo "Failed to built hermesvm.framework for $SLICE in $FLAVOR"
|
||||
echo "Failed to built hermes.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"
|
||||
if [[ -d "$FINAL_PATH/API/hermes/hermes.framework.dSYM" ]]; then
|
||||
echo "Successfully built hermes.framework.dSYM for $SLICE in $FLAVOR"
|
||||
else
|
||||
echo "Failed to built hermesvm.framework.dSYM for $SLICE in $FLAVOR"
|
||||
echo "Failed to built hermes.framework.dSYM for $SLICE in $FLAVOR"
|
||||
echo "Please try again"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -15,6 +15,8 @@ runs:
|
||||
steps:
|
||||
- name: Setup xcode
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Restore Hermes workspace
|
||||
uses: ./.github/actions/restore-hermes-workspace
|
||||
- name: Restore Cached Artifacts
|
||||
@@ -43,8 +45,6 @@ runs:
|
||||
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
|
||||
@@ -186,7 +186,7 @@ runs:
|
||||
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
|
||||
DSYM_FILE_PATH=lib/hermesvm.framework.dSYM
|
||||
DSYM_FILE_PATH=API/hermes/hermes.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/"
|
||||
@@ -197,10 +197,10 @@ runs:
|
||||
cp -r build_xrsimulator/$DSYM_FILE_PATH "$WORKING_DIR/xrsimulator/"
|
||||
|
||||
DEST_DIR="/tmp/hermes/dSYM/$FLAVOR"
|
||||
tar -C "$WORKING_DIR" -czvf "hermesvm.framework.dSYM" .
|
||||
tar -C "$WORKING_DIR" -czvf "hermes.framework.dSYM" .
|
||||
|
||||
mkdir -p "$DEST_DIR"
|
||||
mv "hermesvm.framework.dSYM" "$DEST_DIR"
|
||||
mv "hermes.framework.dSYM" "$DEST_DIR"
|
||||
- name: Upload hermes dSYM artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
|
||||
@@ -99,8 +99,8 @@ runs:
|
||||
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
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Debug/hermes.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-debug.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Release/hermes.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-release.tar.gz
|
||||
- name: Download ReactNativeDependencies
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -116,12 +116,12 @@ runs:
|
||||
- name: Print Artifacts Directory
|
||||
shell: bash
|
||||
run: ls -lR ./packages/react-native/ReactAndroid/external-artifacts/artifacts/
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup gradle
|
||||
uses: ./.github/actions/setup-gradle
|
||||
with:
|
||||
cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }}
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Build packages
|
||||
|
||||
@@ -14,8 +14,6 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Configure Git
|
||||
|
||||
@@ -35,8 +35,6 @@ runs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Start Metro in Debug
|
||||
|
||||
@@ -17,6 +17,9 @@ outputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Setup hermes version
|
||||
shell: bash
|
||||
id: hermes-version
|
||||
@@ -64,8 +67,6 @@ runs:
|
||||
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
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
name: Run Fantom Tests
|
||||
inputs:
|
||||
release-type:
|
||||
required: true
|
||||
description: The type of release we are building. It could be nightly, release or dry-run
|
||||
gradle-cache-encryption-key:
|
||||
description: "The encryption key needed to store the Gradle Configuration cache"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y git cmake openssl libssl-dev clang
|
||||
- name: Setup git safe folders
|
||||
shell: bash
|
||||
run: git config --global --add safe.directory '*'
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Install node dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Setup gradle
|
||||
uses: ./.github/actions/setup-gradle
|
||||
with:
|
||||
cache-read-only: "false"
|
||||
cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }}
|
||||
- name: Restore Fantom ccache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: /github/home/.cache/ccache
|
||||
key: v2-ccache-fantom-${{ github.job }}-${{ github.ref }}-${{ hashFiles(
|
||||
'packages/react-native/ReactAndroid/**/*.cpp',
|
||||
'packages/react-native/ReactAndroid/**/*.h',
|
||||
'packages/react-native/ReactAndroid/**/CMakeLists.txt',
|
||||
'packages/react-native/ReactCommon/**/*.cpp',
|
||||
'packages/react-native/ReactCommon/**/*.h',
|
||||
'packages/react-native/ReactCommon/**/CMakeLists.txt',
|
||||
'private/react-native-fantom/tester/**/*.cpp',
|
||||
'private/react-native-fantom/tester/**/*.h',
|
||||
'private/react-native-fantom/tester/**/CMakeLists.txt'
|
||||
) }}
|
||||
restore-keys: |
|
||||
v2-ccache-fantom-${{ github.job }}-${{ github.ref }}-
|
||||
v2-ccache-fantom-${{ github.job }}-
|
||||
v2-ccache-fantom-
|
||||
- name: Show ccache stats
|
||||
shell: bash
|
||||
run: ccache -s -v
|
||||
- name: Run Fantom Tests
|
||||
shell: bash
|
||||
run: yarn fantom
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
- name: Save Fantom ccache
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, '-stable') }}
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: /github/home/.cache/ccache
|
||||
key: v2-ccache-fantom-${{ github.job }}-${{ github.ref }}-${{ hashFiles(
|
||||
'packages/react-native/ReactAndroid/**/*.cpp',
|
||||
'packages/react-native/ReactAndroid/**/*.h',
|
||||
'packages/react-native/ReactAndroid/**/CMakeLists.txt',
|
||||
'packages/react-native/ReactCommon/**/*.cpp',
|
||||
'packages/react-native/ReactCommon/**/*.h',
|
||||
'packages/react-native/ReactCommon/**/CMakeLists.txt',
|
||||
'private/react-native-fantom/tester/**/*.cpp',
|
||||
'private/react-native-fantom/tester/**/*.h',
|
||||
'private/react-native-fantom/tester/**/CMakeLists.txt'
|
||||
) }}
|
||||
- name: Show ccache stats
|
||||
shell: bash
|
||||
run: ccache -s -v
|
||||
- name: Upload test results
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
name: run-fantom-tests-results
|
||||
compression-level: 1
|
||||
path: |
|
||||
private/react-native-fantom/build/reports
|
||||
@@ -0,0 +1,32 @@
|
||||
name: setup-xcode-build-cache
|
||||
description: Add caching to iOS jobs to speed up builds
|
||||
inputs:
|
||||
hermes-version:
|
||||
description: The version of hermes
|
||||
required: true
|
||||
flavor:
|
||||
description: The flavor that is going to be built
|
||||
default: Debug
|
||||
use-frameworks:
|
||||
description: Whether we are bulding with DynamicFrameworks or StaticLibraries
|
||||
default: StaticLibraries
|
||||
ruby-version:
|
||||
description: The ruby version we are going to use
|
||||
default: 2.6.10
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: See commands.yml with_xcodebuild_cache
|
||||
shell: bash
|
||||
run: echo "See commands.yml with_xcodebuild_cache"
|
||||
- name: Cache podfile lock
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: packages/rn-tester/Podfile.lock
|
||||
key: v13-podfilelock-${{ github.job }}-NewArch-${{ inputs.flavor }}-${{ inputs.use-frameworks }}-${{ inputs.ruby-version }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version }}
|
||||
- name: Cache cocoapods
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: packages/rn-tester/Pods
|
||||
key: v15-cocoapods-${{ github.job }}-NewArch-${{ inputs.flavor }}-${{ inputs.use-frameworks }}-${{ inputs.ruby-version }}-${{ hashfiles('packages/rn-tester/Podfile.lock') }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version}}
|
||||
@@ -23,8 +23,6 @@ runs:
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- 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"
|
||||
@@ -36,6 +34,8 @@ runs:
|
||||
- name: Print Downloaded hermes
|
||||
shell: bash
|
||||
run: ls -lR "$HERMES_WS_DIR"
|
||||
- name: Run yarn
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
|
||||
@@ -102,6 +102,13 @@ runs:
|
||||
- name: Print ReactCore folder
|
||||
shell: bash
|
||||
run: ls -lR /tmp/ReactCore
|
||||
- name: Setup xcode build cache
|
||||
uses: ./.github/actions/setup-xcode-build-cache
|
||||
with:
|
||||
hermes-version: ${{ inputs.hermes-version }}
|
||||
use-frameworks: ${{ inputs.use-frameworks }}
|
||||
flavor: ${{ inputs.flavor }}
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
- name: Install CocoaPods dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
name: test-library-on-nightly
|
||||
description: Tests a library on a nightly
|
||||
inputs:
|
||||
library-npm-package:
|
||||
description: The library npm package to add
|
||||
required: true
|
||||
platform:
|
||||
description: whether we want to build for iOS or Android
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Create new app
|
||||
shell: bash
|
||||
run: |
|
||||
cd /tmp
|
||||
npx @react-native-community/cli init RNApp --skip-install --version nightly
|
||||
- name: Add library
|
||||
shell: bash
|
||||
run: |
|
||||
cd /tmp/RNApp
|
||||
yarn add ${{ inputs.library-npm-package }}
|
||||
|
||||
# iOS
|
||||
- name: Setup xcode
|
||||
if: ${{ inputs.platform == 'ios' }}
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- name: Build iOS
|
||||
shell: bash
|
||||
if: ${{ inputs.platform == 'ios' }}
|
||||
run: |
|
||||
cd /tmp/RNApp/ios
|
||||
bundle install
|
||||
bundle exec pod install
|
||||
xcodebuild build \
|
||||
-workspace RNApp.xcworkspace \
|
||||
-scheme RNApp \
|
||||
-sdk iphonesimulator
|
||||
|
||||
# Android
|
||||
- name: Setup Java for Android
|
||||
if: ${{ inputs.platform == 'android' }}
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
- name: Build Android
|
||||
shell: bash
|
||||
if: ${{ inputs.platform == 'android' }}
|
||||
run: |
|
||||
cd /tmp/RNApp/android
|
||||
./gradlew assembleDebug
|
||||
@@ -2,6 +2,8 @@ name: yarn-install
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -188,7 +188,6 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
|
||||
status: 201,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
id: 1,
|
||||
html_url:
|
||||
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
|
||||
}),
|
||||
@@ -209,11 +208,9 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
|
||||
body: fetchBody,
|
||||
},
|
||||
);
|
||||
expect(response).toEqual({
|
||||
id: 1,
|
||||
html_url:
|
||||
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
|
||||
});
|
||||
expect(response).toEqual(
|
||||
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a draft release for prerelease on GitHub', async () => {
|
||||
@@ -241,7 +238,6 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
|
||||
status: 201,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
id: 1,
|
||||
html_url:
|
||||
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
|
||||
}),
|
||||
@@ -262,11 +258,9 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
|
||||
body: fetchBody,
|
||||
},
|
||||
);
|
||||
expect(response).toEqual({
|
||||
id: 1,
|
||||
html_url:
|
||||
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
|
||||
});
|
||||
expect(response).toEqual(
|
||||
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if the post failes', async () => {
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
prepareFailurePayload,
|
||||
sendMessageToDiscord,
|
||||
} = require('../notifyDiscord');
|
||||
|
||||
describe('prepareFailurePayload', () => {
|
||||
it('should handle undefined failures', () => {
|
||||
const message = prepareFailurePayload(undefined);
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty failures array', () => {
|
||||
const message = prepareFailurePayload([]);
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should format a single failure correctly', () => {
|
||||
const failures = [
|
||||
{
|
||||
library: 'react-native-reanimated',
|
||||
platform: 'iOS',
|
||||
},
|
||||
];
|
||||
|
||||
const message = prepareFailurePayload(failures);
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [iOS] react-native-reanimated',
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort multiple failures by platform and library name', () => {
|
||||
const failures = [
|
||||
{
|
||||
library: 'react-native-reanimated',
|
||||
platform: 'iOS',
|
||||
},
|
||||
{
|
||||
library: 'react-native-gesture-handler',
|
||||
platform: 'Android',
|
||||
},
|
||||
{
|
||||
library: 'react-native-screens',
|
||||
platform: 'iOS',
|
||||
},
|
||||
{
|
||||
library: 'react-native-svg',
|
||||
platform: 'Android',
|
||||
},
|
||||
];
|
||||
|
||||
const message = prepareFailurePayload(failures);
|
||||
|
||||
// The failures should be sorted: first Android (alphabetically), then iOS
|
||||
// Within each platform, libraries should be sorted alphabetically
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [Android] react-native-gesture-handler\n❌ [Android] react-native-svg\n❌ [iOS] react-native-reanimated\n❌ [iOS] react-native-screens',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle failures with missing properties', () => {
|
||||
const failures = [
|
||||
{
|
||||
// Missing library
|
||||
platform: 'iOS',
|
||||
},
|
||||
{
|
||||
library: 'react-native-gesture-handler',
|
||||
// Missing platform
|
||||
},
|
||||
{
|
||||
// Both missing
|
||||
},
|
||||
];
|
||||
|
||||
const message = prepareFailurePayload(failures);
|
||||
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [iOS] Unknown\n❌ [Unknown] react-native-gesture-handler\n❌ [Unknown] Unknown',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendMessageToDiscord', () => {
|
||||
// Store the original fetch function
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
// Setup and teardown for each test
|
||||
beforeEach(() => {
|
||||
// Mock the global fetch function
|
||||
global.fetch = jest.fn();
|
||||
// Silence console logs during tests
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore the original fetch function
|
||||
global.fetch = originalFetch;
|
||||
// Restore console functions
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should throw an error if webhook URL is missing', async () => {
|
||||
await expect(sendMessageToDiscord(null, {})).rejects.toThrow(
|
||||
'Discord webhook URL is missing',
|
||||
);
|
||||
});
|
||||
|
||||
it('should send a message successfully', async () => {
|
||||
// Mock a successful response
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
});
|
||||
|
||||
const webhook = 'https://discord.com/api/webhooks/123/abc';
|
||||
const message = {content: 'Test message'};
|
||||
|
||||
await expect(sendMessageToDiscord(webhook, message)).resolves.not.toThrow();
|
||||
|
||||
// Verify fetch was called with the right arguments
|
||||
expect(global.fetch).toHaveBeenCalledWith(webhook, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
|
||||
// Verify console.log was called
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Successfully sent message to Discord',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the response is not ok', async () => {
|
||||
// Mock a failed response
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 400,
|
||||
text: jest.fn().mockResolvedValueOnce('Bad Request'),
|
||||
});
|
||||
|
||||
const webhook = 'https://discord.com/api/webhooks/123/abc';
|
||||
const message = {content: 'Test message'};
|
||||
|
||||
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
|
||||
'HTTP status code: 400',
|
||||
);
|
||||
|
||||
// Verify console.error was called
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
'Failed to send message to Discord: 400 Bad Request',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if fetch fails', async () => {
|
||||
// Mock a network error
|
||||
const networkError = new Error('Network error');
|
||||
global.fetch.mockRejectedValueOnce(networkError);
|
||||
|
||||
const webhook = 'https://discord.com/api/webhooks/123/abc';
|
||||
const message = {content: 'Test message'};
|
||||
|
||||
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
|
||||
'Network error',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -117,13 +117,13 @@ describe('#verifyPublishedTemplate', () => {
|
||||
it('will timeout if npm does not update package version after a set number of retries', async () => {
|
||||
const RETRIES = 2;
|
||||
|
||||
(await verifyPublishedTemplate('0.77.0', true, RETRIES),
|
||||
await verifyPublishedTemplate('0.77.0', true, RETRIES),
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'@react-native-community/template',
|
||||
'0.77.0',
|
||||
'latest',
|
||||
2,
|
||||
));
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,13 +83,13 @@ describe('#verifyReleaseOnNPM', () => {
|
||||
it('will timeout if npm does not update package version after a set number of retries', async () => {
|
||||
const RETRIES = 2;
|
||||
|
||||
(await verifyReleaseOnNpm('0.77.0', true, RETRIES),
|
||||
await verifyReleaseOnNpm('0.77.0', true, RETRIES),
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'react-native',
|
||||
'0.77.0',
|
||||
'latest',
|
||||
2,
|
||||
));
|
||||
);
|
||||
});
|
||||
|
||||
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {
|
||||
prepareFailurePayload,
|
||||
sendMessageToDiscord,
|
||||
} = require('./notifyDiscord');
|
||||
|
||||
function readOutcomes() {
|
||||
const baseDir = '/tmp';
|
||||
let outcomes = [];
|
||||
fs.readdirSync(baseDir).forEach(file => {
|
||||
const fullPath = path.join(baseDir, file);
|
||||
if (fullPath.endsWith('outcome') && fs.statSync(fullPath).isDirectory) {
|
||||
fs.readdirSync(fullPath).forEach(subFile => {
|
||||
const subFullPath = path.join(fullPath, subFile);
|
||||
if (subFullPath.endsWith('outcome')) {
|
||||
const [library, status] = String(fs.readFileSync(subFullPath, 'utf8'))
|
||||
.trim()
|
||||
.split(':');
|
||||
const platform = subFile.includes('android') ? 'Android' : 'iOS';
|
||||
console.log(
|
||||
`[${platform}] ${library} completed with status ${status}`,
|
||||
);
|
||||
outcomes.push({
|
||||
library: library.trim(),
|
||||
platform,
|
||||
status: status.trim(),
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (fullPath.endsWith('outcome')) {
|
||||
const [library, status] = String(fs.readFileSync(fullPath, 'utf8'))
|
||||
.trim()
|
||||
.split(':');
|
||||
const platform = file.includes('android') ? 'Android' : 'iOS';
|
||||
console.log(`[${platform}] ${library} completed with status ${status}`);
|
||||
outcomes.push({
|
||||
library: library.trim(),
|
||||
platform,
|
||||
status: status.trim(),
|
||||
});
|
||||
}
|
||||
});
|
||||
return outcomes;
|
||||
}
|
||||
|
||||
function printFailures(outcomes) {
|
||||
console.log('Printing failures...');
|
||||
let failedLibraries = [];
|
||||
outcomes.forEach(entry => {
|
||||
if (entry.status !== 'success') {
|
||||
console.log(
|
||||
`❌ [${entry.platform}] ${entry.library} failed with status ${entry.status}`,
|
||||
);
|
||||
failedLibraries.push({
|
||||
library: entry.library,
|
||||
platform: entry.platform,
|
||||
});
|
||||
}
|
||||
});
|
||||
return failedLibraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to Discord with the list of failures.
|
||||
* @param {string} webHook - The Discord webhook URL
|
||||
* @param {Array<Object>} failures - List of failures to report
|
||||
* @returns {Promise<void>} - A promise that resolves when the message is sent
|
||||
*/
|
||||
async function notifyDiscord(webHook, failures) {
|
||||
if (!webHook) {
|
||||
console.error('Discord webhook URL is missing');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!failures || failures.length === 0) {
|
||||
console.log('No failures to report to Discord');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use the prepareFailurePayload function to format the message
|
||||
const message = prepareFailurePayload(failures);
|
||||
|
||||
// Use the sendMessageToDiscord function to send the message
|
||||
await sendMessageToDiscord(webHook, message);
|
||||
} catch (error) {
|
||||
console.error('Error in notifyDiscord function:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function collectResults(discordWebHook) {
|
||||
const outcomes = readOutcomes();
|
||||
const failures = printFailures(outcomes);
|
||||
|
||||
if (failures.length > 0) {
|
||||
if (discordWebHook) {
|
||||
console.log('Sending to discord');
|
||||
await notifyDiscord(discordWebHook, failures);
|
||||
} else {
|
||||
console.log('Web hook not set');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('✅ All tests passed!');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
collectResults,
|
||||
notifyDiscord,
|
||||
};
|
||||
@@ -101,11 +101,7 @@ async function _createDraftReleaseOnGitHub(version, body, latest, token) {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const {html_url, id} = data;
|
||||
return {
|
||||
html_url,
|
||||
id,
|
||||
};
|
||||
return data.html_url;
|
||||
}
|
||||
|
||||
function moveToChangelogBranch(version) {
|
||||
@@ -128,8 +124,7 @@ async function createDraftRelease(version, latest, token) {
|
||||
latest,
|
||||
token,
|
||||
);
|
||||
log(`Created draft release: ${release.html_url}, ID ${release.id}`);
|
||||
return release;
|
||||
log(`Created draft release: ${release}`);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -25,31 +25,6 @@ function extractUsersFromScheduleAndDate(schedule, userMap, date) {
|
||||
return [user1, user2];
|
||||
}
|
||||
|
||||
/**
|
||||
* You can invoke this script by doing:
|
||||
* ```
|
||||
* node .github/workflow-scripts/extractIssueOncalls.js $DATA
|
||||
* ```
|
||||
*
|
||||
* the $DATA is stored in the github secrets as ONCALL_SCHEDULE variable.
|
||||
* The format of the data is:
|
||||
* ```
|
||||
* {
|
||||
* \"userMap\": {
|
||||
* \"discord_handle1\": \"discord_id1\",
|
||||
* \"discord_handle2\": \"discord_id2\",
|
||||
* ...
|
||||
* },
|
||||
* \"schedule\": {
|
||||
* \"2025-07-29\": [\"discord_handle1\", \"discord_handle2\"],
|
||||
* \"2025-08-05\": [\"discord_handle3\", \"discord_handle4\"],
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* When uploading the secret, make sure that the JSON strings are escaped!
|
||||
* The script will fail otherwise, because GitHub will remove the `"` characters.
|
||||
*/
|
||||
function main() {
|
||||
const configuration = process.argv[2];
|
||||
const {userMap, schedule} = JSON.parse(configuration);
|
||||
|
||||
@@ -87,11 +87,11 @@ async function launchAppOnSimulator(appId, udid, isDebug) {
|
||||
|
||||
function startVideoRecording(jsengine, currentAttempt) {
|
||||
console.log(
|
||||
`Start video record using pid: video_record_${currentAttempt}.pid`,
|
||||
`Start video record using pid: video_record_${jsengine}_${currentAttempt}.pid`,
|
||||
);
|
||||
|
||||
const recordingArgs =
|
||||
`simctl io booted recordVideo video_record_${currentAttempt}.mov`.split(
|
||||
`simctl io booted recordVideo video_record_${jsengine}_${currentAttempt}.mov`.split(
|
||||
' ',
|
||||
);
|
||||
const recordingProcess = childProcess.spawn('xcrun', recordingArgs, {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sends a message to Discord using the webhook URL.
|
||||
* @param {string} webHook - The Discord webhook URL
|
||||
* @param {Object} message - The message to send
|
||||
* @returns {Promise<void>} - A promise that resolves when the message is sent
|
||||
*/
|
||||
async function sendMessageToDiscord(webHook, message) {
|
||||
if (!webHook) {
|
||||
throw new Error('Discord webhook URL is missing');
|
||||
}
|
||||
|
||||
// Send the request using fetch
|
||||
const response = await fetch(webHook, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
|
||||
// Handle the response
|
||||
if (response.ok) {
|
||||
console.log('Successfully sent message to Discord');
|
||||
return;
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
console.error(
|
||||
`Failed to send message to Discord: ${response.status} ${errorText}`,
|
||||
);
|
||||
throw new Error(`HTTP status code: ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a formatted Discord message payload from a list of failures.
|
||||
* @param {Array<Object>} failures - List of failures to format
|
||||
* @returns {Object} - The formatted Discord message payload
|
||||
*/
|
||||
function prepareFailurePayload(failures) {
|
||||
if (!failures || failures.length === 0) {
|
||||
return {
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
|
||||
};
|
||||
}
|
||||
|
||||
// Sort failures by platform and then by library name
|
||||
const sortedFailures = [...failures].sort((a, b) => {
|
||||
// First sort by platform
|
||||
const platformA = a.platform || 'Unknown';
|
||||
const platformB = b.platform || 'Unknown';
|
||||
|
||||
if (platformA !== platformB) {
|
||||
return platformA.localeCompare(platformB);
|
||||
}
|
||||
|
||||
// Then sort by library name
|
||||
const libraryA = a.library || 'Unknown';
|
||||
const libraryB = b.library || 'Unknown';
|
||||
return libraryA.localeCompare(libraryB);
|
||||
});
|
||||
|
||||
// Format the failures into a message
|
||||
const formattedFailures = sortedFailures
|
||||
.map(failure => {
|
||||
const library = failure.library || 'Unknown';
|
||||
const platform = failure.platform || 'Unknown';
|
||||
return `❌ [${platform}] ${library}`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
content: `⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n${formattedFailures}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Export the functions using CommonJS syntax
|
||||
module.exports = {
|
||||
prepareFailurePayload,
|
||||
sendMessageToDiscord,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
# This jobs runs every day 2 hours after the nightly job and its purpose is to report
|
||||
# a failure in case the nightly failed to be published. We are going to hook this to an internal automation.
|
||||
name: Check Nightlies
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# nightly build @ 4:15 AM UTC
|
||||
schedule:
|
||||
- cron: '15 4 * * *'
|
||||
|
||||
jobs:
|
||||
check-nightly:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Check nightly
|
||||
run: |
|
||||
TODAY=$(date "+%Y%m%d")
|
||||
echo "Checking nightly for $TODAY"
|
||||
NIGHTLY="$(npm view react-native | grep $TODAY)"
|
||||
if [[ -z $NIGHTLY ]]; then
|
||||
echo 'Nightly job failed.'
|
||||
exit 1
|
||||
else
|
||||
echo 'Nightly Worked, All Good!'
|
||||
fi
|
||||
|
||||
test-libraries:
|
||||
uses: ./.github/workflows/test-libraries-on-nightlies.yml
|
||||
needs: check-nightly
|
||||
secrets:
|
||||
discord_webhook_url: ${{ secrets.NIGHTLY_DISCORD_WEBHOOK }}
|
||||
@@ -21,24 +21,9 @@ jobs:
|
||||
git config --local user.name "React Native Bot"
|
||||
- name: Create draft release
|
||||
uses: actions/github-script@v6
|
||||
id: create-draft-release
|
||||
with:
|
||||
script: |
|
||||
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
|
||||
const version = '${{ github.ref_name }}';
|
||||
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
|
||||
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}')).id;
|
||||
result-encoding: string
|
||||
- name: Upload release assets for DotSlash
|
||||
uses: actions/github-script@v6
|
||||
env:
|
||||
RELEASE_ID: ${{ steps.create-draft-release.outputs.result }}
|
||||
with:
|
||||
script: |
|
||||
const {uploadReleaseAssetsForDotSlashFiles} = require('./scripts/releases/upload-release-assets-for-dotslash.js');
|
||||
const version = '${{ github.ref_name }}';
|
||||
await uploadReleaseAssetsForDotSlashFiles({
|
||||
version,
|
||||
token: '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}',
|
||||
releaseId: process.env.RELEASE_ID,
|
||||
});
|
||||
await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}');
|
||||
|
||||
@@ -19,7 +19,6 @@ on:
|
||||
|
||||
jobs:
|
||||
create_release:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -27,6 +27,10 @@ jobs:
|
||||
ONCALL2=$(echo $ONCALLS | cut -d ' ' -f 2)
|
||||
echo "oncall1=$ONCALL1" >> $GITHUB_ENV
|
||||
echo "oncall2=$ONCALL2" >> $GITHUB_ENV
|
||||
- name: Print oncalls
|
||||
run: |
|
||||
echo "oncall1: ${{ env.oncall1 }}"
|
||||
echo "oncall2: ${{ env.oncall2 }}"
|
||||
- name: Monitor New Issues
|
||||
uses: react-native-community/repo-monitor@v1.0.1
|
||||
with:
|
||||
|
||||
@@ -23,7 +23,6 @@ jobs:
|
||||
|
||||
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
|
||||
@@ -51,8 +50,8 @@ jobs:
|
||||
- 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 }}
|
||||
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
|
||||
@@ -76,7 +75,7 @@ jobs:
|
||||
uses: ./.github/actions/build-apple-slices-hermes
|
||||
with:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
slice: ${{ matrix.slice }}
|
||||
slice: ${{ matrix.slice}}
|
||||
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
|
||||
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
|
||||
|
||||
@@ -102,7 +101,6 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
prebuild_apple_dependencies:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
uses: ./.github/workflows/prebuild-ios-dependencies.yml
|
||||
secrets: inherit
|
||||
|
||||
@@ -147,7 +145,6 @@ jobs:
|
||||
|
||||
build_android:
|
||||
runs-on: 8-core-ubuntu
|
||||
if: github.repository == 'facebook/react-native'
|
||||
needs: [set_release_type]
|
||||
container:
|
||||
image: reactnativecommunity/react-native-android:latest
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
id: restore-ios-slice
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: v3-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/*.js', 'packages/react-native/scripts/ios-prebuild.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
key: v3-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/setup.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
path: packages/react-native/
|
||||
- name: Setup node.js
|
||||
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
uses: actions/cache/save@v4
|
||||
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
|
||||
with:
|
||||
key: v3-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/*.js', 'packages/react-native/scripts/ios-prebuild.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
key: v3-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/setup.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
path: |
|
||||
packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products
|
||||
packages/react-native/.build/headers
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: packages/react-native/.build/output/xcframeworks
|
||||
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/*.js', 'packages/react-native/scripts/ios-prebuild.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/setup.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
- name: Setup node.js
|
||||
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/setup-node
|
||||
@@ -209,4 +209,4 @@ jobs:
|
||||
path: |
|
||||
packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.xcframework.tar.gz
|
||||
packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.framework.dSYM.tar.gz
|
||||
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/*.js', 'packages/react-native/scripts/ios-prebuild.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/setup.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}
|
||||
|
||||
@@ -179,9 +179,8 @@ jobs:
|
||||
- name: Compress and Rename dSYM
|
||||
if: steps.restore-xcframework.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd packages/react-native/third-party/Symbols/
|
||||
tar -cz -f ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz .
|
||||
mv ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz ./ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
|
||||
tar -cz -f packages/react-native/third-party/Symbols/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz \
|
||||
packages/react-native/third-party/Symbols/ReactNativeDependencies.framework.dSYM
|
||||
- name: Upload XCFramework Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -9,7 +9,6 @@ on:
|
||||
jobs:
|
||||
publish_bumped_packages:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
env:
|
||||
GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }}
|
||||
steps:
|
||||
|
||||
@@ -21,7 +21,6 @@ jobs:
|
||||
|
||||
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
|
||||
@@ -99,7 +98,6 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
prebuild_apple_dependencies:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
uses: ./.github/workflows/prebuild-ios-dependencies.yml
|
||||
secrets: inherit
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ on:
|
||||
jobs:
|
||||
rerun:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- name: rerun ${{ inputs.run_id }}
|
||||
env:
|
||||
|
||||
@@ -2,6 +2,11 @@ name: Test All
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run-e2e-tests:
|
||||
description: Whether to run E2E tests or not
|
||||
type: boolean
|
||||
default: false
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
@@ -11,7 +16,6 @@ on:
|
||||
jobs:
|
||||
set_release_type:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
outputs:
|
||||
RELEASE_TYPE: ${{ steps.set_release_type.outputs.RELEASE_TYPE }}
|
||||
env:
|
||||
@@ -35,7 +39,6 @@ jobs:
|
||||
|
||||
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
|
||||
@@ -114,7 +117,6 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
prebuild_apple_dependencies:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
uses: ./.github/workflows/prebuild-ios-dependencies.yml
|
||||
secrets: inherit
|
||||
|
||||
@@ -166,6 +168,7 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
test_e2e_ios_rntester:
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
runs-on: macos-14-large
|
||||
needs:
|
||||
[test_ios_rntester]
|
||||
@@ -199,6 +202,7 @@ jobs:
|
||||
flavor: ${{ matrix.flavor }}
|
||||
|
||||
test_e2e_ios_templateapp:
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
runs-on: macos-14-large
|
||||
needs: [build_npm_package, prebuild_apple_dependencies]
|
||||
env:
|
||||
@@ -249,11 +253,6 @@ jobs:
|
||||
- name: Print ReactCore folder
|
||||
shell: bash
|
||||
run: ls -lR /tmp/ReactCore
|
||||
- name: Configure git
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global user.email "react-native-bot@meta.com"
|
||||
git config --global user.name "React Native Bot"
|
||||
- name: Prepare artifacts
|
||||
run: |
|
||||
REACT_NATIVE_PKG=$(find /tmp/react-native-tmp -type f -name "*.tgz")
|
||||
@@ -296,6 +295,7 @@ jobs:
|
||||
working-directory: /tmp/RNTestProject
|
||||
|
||||
test_e2e_android_templateapp:
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
runs-on: 4-core-ubuntu
|
||||
needs: build_npm_package
|
||||
strategy:
|
||||
@@ -378,26 +378,6 @@ jobs:
|
||||
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]
|
||||
container:
|
||||
# Version is pinned to v18.0 to unblock `run_fantom_tests` - see https://github.com/react-native-community/docker-android/pull/242#issuecomment-3280029122
|
||||
image: reactnativecommunity/react-native-android:v18.0
|
||||
env:
|
||||
TERM: "dumb"
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
|
||||
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Test Fantom
|
||||
uses: ./.github/actions/run-fantom-tests
|
||||
with:
|
||||
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
|
||||
@@ -434,9 +414,11 @@ jobs:
|
||||
uses: ./.github/actions/build-android
|
||||
with:
|
||||
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
|
||||
run-e2e-tests: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
|
||||
|
||||
test_e2e_android_rntester:
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
runs-on: 4-core-ubuntu
|
||||
needs: [build_android]
|
||||
strategy:
|
||||
@@ -598,11 +580,10 @@ jobs:
|
||||
|
||||
test_js:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: ["24.4.1", "22", "20.19.4"]
|
||||
node-version: ["24", "22", "20"]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -613,7 +594,6 @@ jobs:
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
@@ -636,7 +616,7 @@ jobs:
|
||||
rerun-failed-jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_e2e_ios_rntester, test_e2e_android_rntester, test_e2e_ios_templateapp, test_e2e_android_templateapp]
|
||||
if: ${{ github.ref == 'refs/heads/main' && always() }}
|
||||
if: always()
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
name: Test Libraries on Nightlies
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
discord_webhook_url:
|
||||
required: true
|
||||
|
||||
|
||||
# We use the matrix.library entry to specify the dependency we want to use
|
||||
# The key is used directly as the <pkg> in the `yarn add <pkg>` command.
|
||||
jobs:
|
||||
runner-setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runners: '{"ios":"macos-14-large", "android": "ubuntu-latest"}'
|
||||
steps:
|
||||
- run: echo no-op
|
||||
|
||||
test-library-on-nightly:
|
||||
name: "[${{ matrix.platform }}] ${{ matrix.library }}"
|
||||
needs: runner-setup
|
||||
runs-on: ${{ fromJSON(needs.runner-setup.outputs.runners)[matrix.platform] }}
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
library: [
|
||||
"react-native-async-storage",
|
||||
"react-native-blob-util",
|
||||
"@react-native-clipboard/clipboard",
|
||||
"@react-native-community/datetimepicker",
|
||||
"react-native-gesture-handler",
|
||||
"react-native-image-picker",
|
||||
"react-native-linear-gradient",
|
||||
"@react-native-masked-view/masked-view",
|
||||
# "react-native-maps", React Native Maps with the New Arch support has a complex cocoapods setup for iOS. It needs a dedicated workflow.
|
||||
"@react-native-community/netinfo",
|
||||
"react-native-reanimated@nightly react-native-worklets@nightly", #reanimated requires worklet to be explicitly installed as a separate package
|
||||
"react-native-svg",
|
||||
"react-native-video",
|
||||
"react-native-webview",
|
||||
"react-native-mmkv",
|
||||
"react-native-screens",
|
||||
"react-native-pager-view",
|
||||
"@react-native-community/slider",
|
||||
# additional OSS libs used internally
|
||||
"scandit-react-native-datacapture-barcode scandit-react-native-datacapture-core",
|
||||
"react-native-contacts",
|
||||
"react-native-device-info",
|
||||
"react-native-email-link",
|
||||
"@dr.pogodin/react-native-fs",
|
||||
"react-native-permissions",
|
||||
"react-native-vector-icons",
|
||||
"react-native-masked-view",
|
||||
"@react-native-community/image-editor",
|
||||
]
|
||||
platform: [ios, android]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Test ${{ matrix.library }}
|
||||
id: run-test
|
||||
uses: ./.github/actions/test-library-on-nightly
|
||||
with:
|
||||
library-npm-package: ${{ matrix.library }}
|
||||
platform: ${{ matrix.platform}}
|
||||
- name: Save outcome
|
||||
id: save-outcome
|
||||
if: always()
|
||||
run: |
|
||||
LIB_FOLDER=$(echo "${{matrix.library}}" | tr ' ' '_' | tr '/' '_')
|
||||
echo "${{matrix.library}}: ${{steps.run-test.outcome}}" > "/tmp/$LIB_FOLDER-${{ matrix.platform }}-outcome"
|
||||
echo "lib_folder=$LIB_FOLDER" >> $GITHUB_OUTPUT
|
||||
- name: Upload Artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.save-outcome.outputs.lib_folder }}-${{ matrix.platform }}-outcome
|
||||
path: /tmp/${{ steps.save-outcome.outputs.lib_folder }}-${{ matrix.platform }}-outcome
|
||||
|
||||
|
||||
collect-results:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test-library-on-nightly]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Restore outcomes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: '*-outcome'
|
||||
path: /tmp
|
||||
- name: Collect failures
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const {collectResults} = require('./.github/workflow-scripts/collectNightlyOutcomes.js');
|
||||
await collectResults('${{secrets.discord_webhook_url}}');
|
||||
@@ -1,49 +0,0 @@
|
||||
name: Validate DotSlash Artifacts
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- packages/debugger-shell/bin/react-native-devtools
|
||||
- "scripts/releases/**"
|
||||
- package.json
|
||||
- yarn.lock
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- packages/debugger-shell/bin/react-native-devtools
|
||||
- "scripts/releases/**"
|
||||
- package.json
|
||||
- yarn.lock
|
||||
# Same time as the nightly build: 2:15 AM UTC
|
||||
schedule:
|
||||
- cron: "15 2 * * *"
|
||||
|
||||
jobs:
|
||||
validate-dotslash-artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Configure Git
|
||||
shell: bash
|
||||
run: |
|
||||
git config --local user.email "bot@reactnative.dev"
|
||||
git config --local user.name "React Native Bot"
|
||||
- name: Validate DotSlash artifacts
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const {validateDotSlashArtifacts} = require('./scripts/releases/validate-dotslash-artifacts.js');
|
||||
await validateDotSlashArtifacts();
|
||||
+1
-1
@@ -43,6 +43,7 @@ project.xcworkspace
|
||||
/private/helloworld/android/app/build/
|
||||
/private/helloworld/android/build/
|
||||
/packages/react-native-popup-menu-android/android/build/
|
||||
/packages/react-native-test-library/android/build/
|
||||
|
||||
# Buck
|
||||
.buckd
|
||||
@@ -170,7 +171,6 @@ fix_*.patch
|
||||
|
||||
# Jest Integration
|
||||
/private/react-native-fantom/build/
|
||||
/private/react-native-fantom/.out/
|
||||
/private/react-native-fantom/tester/build/
|
||||
|
||||
# [Experimental] Generated TS type definitions
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"bracketSameLine": true,
|
||||
"bracketSpacing": false,
|
||||
"requirePragma": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "lf",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.code-workspace"],
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.js.flow"
|
||||
],
|
||||
"options": {
|
||||
"parser": "hermes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/__docs__/*.md"
|
||||
],
|
||||
"options": {
|
||||
"parser": "markdown",
|
||||
"proseWrap": "always",
|
||||
"requirePragma": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,46 +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.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
let plugins = ['prettier-plugin-hermes-parser'];
|
||||
try {
|
||||
plugins = require('./.prettier-plugins.fb.js');
|
||||
} catch {}
|
||||
|
||||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSameLine: true,
|
||||
bracketSpacing: false,
|
||||
requirePragma: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
endOfLine: 'lf',
|
||||
plugins,
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.code-workspace'],
|
||||
options: {
|
||||
parser: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.js', '*.js.flow'],
|
||||
options: {
|
||||
parser: 'hermes',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/__docs__/*.md'],
|
||||
options: {
|
||||
parser: 'markdown',
|
||||
proseWrap: 'always',
|
||||
requirePragma: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,27 +1,5 @@
|
||||
# Changelog (pre 0.80)
|
||||
|
||||
## v0.79.6
|
||||
|
||||
### Added
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **RNGP** Add support for `exclusiveEnterpriseRepository` ([df5ac988ce](https://github.com/facebook/react-native/commit/df5ac988cec936c430d41b0fcc15181dc06e46a1) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Cocoapods:** Add the ENTERPRISE_REPOSITORY env var to let user consume artifacts from their personal maven mirror ([a74d930c93](https://github.com/facebook/react-native/commit/a74d930c93ffae8c02142e8cc016a4c390a5f784) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Codegen:** Add missing Babel dependencies ([bf2c3af93b](https://github.com/facebook/react-native/commit/bf2c3af93b146943cb35866fa9badcd188e63f5b) by [@tido64](https://github.com/tido64))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Legacy Arch:** Fix Legacy arch crashing or freezing upon reload ([db600b2e9e](https://github.com/facebook/react-native/commit/db600b2e9e87863cad6dd5ce262dc1f793bcaeb0) by [@robhogan](https://github.com/robhogan))
|
||||
- **Modal:** Fix Modal first frame being rendered on top-left corner ([5a315f8d6b](https://github.com/facebook/react-native/commit/5a315f8d6b0ea54442c7ef94b7346b0c73fd0b4c) by [@cortinico](https://github.com/cortinico))
|
||||
- **TurboModule:** Fix emitting event from turbo module crashes on 32bit android ([43bc43e5e8](https://github.com/facebook/react-native/commit/43bc43e5e85519d2924c4fc80765e66d0c48b1a9) by [@vladimirivanoviliev](https://github.com/vladimirivanoviliev))
|
||||
|
||||
## v0.79.5
|
||||
|
||||
### Fixed
|
||||
@@ -544,37 +522,6 @@ ChuiHW))
|
||||
- **Style:** Fixed `centerContent` losing taps and causing jitter ([fe7e97a2fd](https://github.com/facebook/react-native/commit/fe7e97a2fd272db0d9d9aa7d0561337a7c8e2c30) by [@gaearon](https://github.com/gaearon))
|
||||
- **Xcode:** Properly escape paths in Xcode build script used when bundling an app. ([2fee13094b](https://github.com/facebook/react-native/commit/2fee13094b3d384c071978776fd8b7cff0b6530f) by [@kraenhansen](https://github.com/kraenhansen))
|
||||
|
||||
# Changelog
|
||||
## v0.77.3
|
||||
|
||||
### Added
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Gradle**: RNGP - Add support for `exclusiveEnterpriseRepository` to specify an internal Maven mirror. ([6cb8dc37c7](https://github.com/facebook/react-native/commit/6cb8dc37c74995cba3f9f0a845919f305de53c3d) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
### Changed
|
||||
|
||||
- **Metro**: Bump Metro minimum version from `^0.81.3` to `^0.81.5`. ([dfa81638dd](https://github.com/facebook/react-native/commit/dfa81638dd17e46f70f10b25c4f4fd9f370a4b0e) by [@robhogan](https://github.com/robhogan))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Timers**: Align timer IDs and timer function argument error handling with web standards. ([480a4642e5](https://github.com/facebook/react-native/commit/480a4642e5a644becf1c477d3d239f9b57efff3a) by [@kitten](https://github.com/kitten))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Modal**: Fix Modal first frame being rendered on top-left corner. ([b950fa2afb](https://github.com/facebook/react-native/commit/b950fa2afb20e2213ff6c733cb1c2465b90406ef) by [@cortinico](https://github.com/cortinico))
|
||||
- **layout**: Fix wrong `borderBottomEndRadius` on RTL. ([68d6ada448](https://github.com/facebook/react-native/commit/68d6ada44893701b6006a6b1753131c7e880a30a) by [@riteshshukla04](https://github.com/riteshshukla04))
|
||||
- **Modal**: Fix `FLAG_SECURE` not respected in Modal dialog. ([7e029b0dcf](https://github.com/facebook/react-native/commit/7e029b0dcf6d1a6455a8a6343457b70e353d0ff6) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
- **lifecylcle**: Legacy arch: fix #50274, Fast Refresh sometimes breaks after full refresh. ([c43952ac22](https://github.com/facebook/react-native/commit/c43952ac22b2356be3130c906329f61e246082cb) by [@robhogan](https://github.com/robhogan))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Interop Layer**: Fixed adding child views to a native view using the interop layer. ([d53a60dd23](https://github.com/facebook/react-native/commit/d53a60dd23c5df8afca058a867c50df8b61f62e2) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Debugger**: Restore "Paused in debugger" overlay icon. ([f30c46efbd](https://github.com/facebook/react-native/commit/f30c46efbd964d367f678181589865a3faa931cd) by [@robhogan](https://github.com/robhogan))
|
||||
- **layout**: Layout direction changes are now honored on bundle reload. ([36f29beac4](https://github.com/facebook/react-native/commit/36f29beac47259768612bf56e5d9acfa4b94ab1a) by [@chrsmys](https://github.com/chrsmys))
|
||||
- **file reads**: Fix crash caused by `[RCTFileRequestHanlder invalidate]`. ([789ed7d5ad](https://github.com/facebook/react-native/commit/789ed7d5ad75ad4c20ecd1eb19d1fc18275fc500) by [@zhouzh1](https://github.com/zhouzh1))
|
||||
|
||||
## v0.77.2
|
||||
|
||||
### Added
|
||||
|
||||
+301
-486
File diff suppressed because it is too large
Load Diff
@@ -17,13 +17,10 @@
|
||||
<img src="https://img.shields.io/npm/v/react-native?color=brightgreen&label=npm%20package" alt="Current npm package version." />
|
||||
</a>
|
||||
<a href="https://reactnative.dev/docs/contributing">
|
||||
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs are welcome!" />
|
||||
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome!" />
|
||||
</a>
|
||||
<a href="https://twitter.com/intent/follow?screen_name=reactnative">
|
||||
<img src="https://img.shields.io/twitter/follow/reactnative.svg?label=Follow%20@reactnative" alt="Follow @reactnative on X" />
|
||||
</a>
|
||||
<a href="https://bsky.app/profile/reactnative.dev">
|
||||
<img src="https://img.shields.io/badge/Bluesky-0285FF?logo=bluesky&logoColor=fff" alt="Follow @reactnative.dev on Bluesky" />
|
||||
<img src="https://img.shields.io/twitter/follow/reactnative.svg?label=Follow%20@reactnative" alt="Follow @reactnative" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
+12
-36
@@ -26,12 +26,10 @@ fun getListReactAndroidProperty(name: String) = reactAndroidProperties.getProper
|
||||
|
||||
apiValidation {
|
||||
ignoredPackages.addAll(
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.ignoredPackages")
|
||||
)
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.ignoredPackages"))
|
||||
ignoredClasses.addAll(getListReactAndroidProperty("binaryCompatibilityValidator.ignoredClasses"))
|
||||
nonPublicMarkers.addAll(
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.nonPublicMarkers")
|
||||
)
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.nonPublicMarkers"))
|
||||
validationDisabled =
|
||||
reactAndroidProperties
|
||||
.getProperty("binaryCompatibilityValidator.validationDisabled")
|
||||
@@ -39,9 +37,8 @@ apiValidation {
|
||||
}
|
||||
|
||||
version =
|
||||
if (
|
||||
project.hasProperty("isSnapshot") && (project.property("isSnapshot") as? String).toBoolean()
|
||||
) {
|
||||
if (project.hasProperty("isSnapshot") &&
|
||||
(project.property("isSnapshot") as? String).toBoolean()) {
|
||||
"${reactAndroidProperties.getProperty("VERSION_NAME")}-SNAPSHOT"
|
||||
} else {
|
||||
reactAndroidProperties.getProperty("VERSION_NAME")
|
||||
@@ -69,10 +66,8 @@ tasks.register("clean", Delete::class.java) {
|
||||
description = "Remove all the build files and intermediate build outputs"
|
||||
dependsOn(gradle.includedBuild("gradle-plugin").task(":clean"))
|
||||
subprojects.forEach {
|
||||
if (
|
||||
it.project.plugins.hasPlugin("com.android.library") ||
|
||||
it.project.plugins.hasPlugin("com.android.application")
|
||||
) {
|
||||
if (it.project.plugins.hasPlugin("com.android.library") ||
|
||||
it.project.plugins.hasPlugin("com.android.application")) {
|
||||
dependsOn(it.tasks.named("clean"))
|
||||
}
|
||||
}
|
||||
@@ -82,13 +77,10 @@ tasks.register("clean", Delete::class.java) {
|
||||
delete(rootProject.file("./packages/react-native/sdks/download/"))
|
||||
delete(rootProject.file("./packages/react-native/sdks/hermes/"))
|
||||
delete(
|
||||
rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/arm64-v8a/")
|
||||
)
|
||||
rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/arm64-v8a/"))
|
||||
delete(
|
||||
rootProject.file(
|
||||
"./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/"
|
||||
)
|
||||
)
|
||||
"./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/"))
|
||||
delete(rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/x86/"))
|
||||
delete(rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/x86_64/"))
|
||||
delete(rootProject.file("./packages/react-native-codegen/lib"))
|
||||
@@ -106,8 +98,7 @@ tasks.register("publishAllToMavenTempLocal") {
|
||||
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"
|
||||
)
|
||||
":packages:react-native:ReactAndroid:hermes-engine:publishAllPublicationsToMavenTempLocalRepository")
|
||||
}
|
||||
|
||||
tasks.register("publishAndroidToSonatype") {
|
||||
@@ -129,13 +120,11 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
|
||||
That's fine for local development, but you should not commit this change.
|
||||
********************************************************************************
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
allprojects {
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(project(":packages:react-native:ReactAndroid:hermes-engine"))
|
||||
// TODO: T237406039 update coordinates
|
||||
.using(module("com.facebook.react:hermes-android:0.+"))
|
||||
.because("Users opted to use hermes from nightly")
|
||||
}
|
||||
@@ -163,22 +152,9 @@ allprojects {
|
||||
"**/build/**",
|
||||
"**/hermes-engine/**",
|
||||
"**/internal/featureflags/**",
|
||||
"**/systeminfo/ReactNativeVersion.kt",
|
||||
)
|
||||
"**/systeminfo/ReactNativeVersion.kt")
|
||||
listOf(
|
||||
com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask::class,
|
||||
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class,
|
||||
)
|
||||
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class)
|
||||
.forEach { tasks.withType(it) { exclude(excludePatterns) } }
|
||||
|
||||
// Disable the problematic ktfmt script tasks due to symbolic link issues in subprojects
|
||||
afterEvaluate {
|
||||
listOf("ktfmtCheckScripts", "ktfmtFormatScripts").forEach {
|
||||
tasks.findByName(it)?.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We intentionally disable the `ktfmtCheck` tasks as the formatting is primarly handled inside
|
||||
// fbsource
|
||||
allprojects { tasks.withType<com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask>() { enabled = false } }
|
||||
|
||||
-46
@@ -1,46 +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
|
||||
*/
|
||||
|
||||
declare module '@expo/spawn-async' {
|
||||
type SpawnOptions = {
|
||||
cwd?: string,
|
||||
env?: Object,
|
||||
argv0?: string,
|
||||
stdio?: string | Array<any>,
|
||||
detached?: boolean,
|
||||
uid?: number,
|
||||
gid?: number,
|
||||
shell?: boolean | string,
|
||||
windowsVerbatimArguments?: boolean,
|
||||
windowsHide?: boolean,
|
||||
encoding?: string,
|
||||
ignoreStdio?: boolean,
|
||||
};
|
||||
|
||||
declare class SpawnPromise<T> extends Promise<T> {
|
||||
child: child_process$ChildProcess;
|
||||
}
|
||||
type SpawnResult = {
|
||||
pid?: number,
|
||||
output: string[],
|
||||
stdout: string,
|
||||
stderr: string,
|
||||
status: number | null,
|
||||
signal: string | null,
|
||||
};
|
||||
|
||||
declare function spawnAsync(
|
||||
command: string,
|
||||
args?: $ReadOnlyArray<string>,
|
||||
options?: SpawnOptions,
|
||||
): SpawnPromise<SpawnResult>;
|
||||
|
||||
declare module.exports: typeof spawnAsync;
|
||||
}
|
||||
-61
@@ -1,61 +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
|
||||
*/
|
||||
|
||||
// Partial types for Octokit based on the usage in react-native-github
|
||||
declare module '@octokit/rest' {
|
||||
declare class Octokit {
|
||||
constructor(options?: {auth?: string, ...}): this;
|
||||
|
||||
repos: $ReadOnly<{
|
||||
listReleaseAssets: (
|
||||
params: $ReadOnly<{
|
||||
owner: string,
|
||||
repo: string,
|
||||
release_id: string,
|
||||
}>,
|
||||
) => Promise<{
|
||||
data: Array<{
|
||||
id: string,
|
||||
name: string,
|
||||
...
|
||||
}>,
|
||||
...
|
||||
}>,
|
||||
uploadReleaseAsset: (
|
||||
params: $ReadOnly<{
|
||||
owner: string,
|
||||
repo: string,
|
||||
release_id: string,
|
||||
name: string,
|
||||
data: Buffer,
|
||||
headers: $ReadOnly<{
|
||||
'content-type': string,
|
||||
...
|
||||
}>,
|
||||
...
|
||||
}>,
|
||||
) => Promise<{
|
||||
data: {
|
||||
browser_download_url: string,
|
||||
...
|
||||
},
|
||||
...
|
||||
}>,
|
||||
deleteReleaseAsset: (params: {
|
||||
owner: string,
|
||||
repo: string,
|
||||
asset_id: string,
|
||||
...
|
||||
}) => Promise<mixed>,
|
||||
}>;
|
||||
}
|
||||
|
||||
declare export {Octokit};
|
||||
}
|
||||
Vendored
-13
@@ -1,13 +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
|
||||
*/
|
||||
|
||||
declare module 'fb-dotslash' {
|
||||
declare module.exports: string;
|
||||
}
|
||||
-421
@@ -1,421 +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
|
||||
*/
|
||||
|
||||
declare module 'jsonc-parser' {
|
||||
/**
|
||||
* Creates a JSON scanner on the given text.
|
||||
* If ignoreTrivia is set, whitespaces or comments are ignored.
|
||||
*/
|
||||
declare export const createScanner: (
|
||||
text: string,
|
||||
ignoreTrivia?: boolean,
|
||||
) => JSONScanner;
|
||||
export type ScanError = number;
|
||||
export type SyntaxKind = number;
|
||||
/**
|
||||
* The scanner object, representing a JSON scanner at a position in the input string.
|
||||
*/
|
||||
export type JSONScanner = $ReadOnly<{
|
||||
/**
|
||||
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
|
||||
*/
|
||||
setPosition(pos: number): void,
|
||||
/**
|
||||
* Read the next token. Returns the token code.
|
||||
*/
|
||||
scan(): SyntaxKind,
|
||||
/**
|
||||
* Returns the zero-based current scan position, which is after the last read token.
|
||||
*/
|
||||
getPosition(): number,
|
||||
/**
|
||||
* Returns the last read token.
|
||||
*/
|
||||
getToken(): SyntaxKind,
|
||||
/**
|
||||
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
|
||||
*/
|
||||
getTokenValue(): string,
|
||||
/**
|
||||
* The zero-based start offset of the last read token.
|
||||
*/
|
||||
getTokenOffset(): number,
|
||||
/**
|
||||
* The length of the last read token.
|
||||
*/
|
||||
getTokenLength(): number,
|
||||
/**
|
||||
* The zero-based start line number of the last read token.
|
||||
*/
|
||||
getTokenStartLine(): number,
|
||||
/**
|
||||
* The zero-based start character (column) of the last read token.
|
||||
*/
|
||||
getTokenStartCharacter(): number,
|
||||
/**
|
||||
* An error code of the last scan.
|
||||
*/
|
||||
getTokenError(): ScanError,
|
||||
}>;
|
||||
/**
|
||||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
|
||||
*/
|
||||
declare export const getLocation: (
|
||||
text: string,
|
||||
position: number,
|
||||
) => Location;
|
||||
/**
|
||||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
||||
* Therefore, always check the errors list to find out if the input was valid.
|
||||
*/
|
||||
declare export const parse: (
|
||||
text: string,
|
||||
errors?: ParseError[],
|
||||
options?: ParseOptions,
|
||||
) => any;
|
||||
/**
|
||||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
||||
*/
|
||||
declare export const parseTree: (
|
||||
text: string,
|
||||
errors?: ParseError[],
|
||||
options?: ParseOptions,
|
||||
) => Node | void;
|
||||
/**
|
||||
* Finds the node at the given path in a JSON DOM.
|
||||
*/
|
||||
declare export const findNodeAtLocation: (
|
||||
root: Node,
|
||||
path: JSONPath,
|
||||
) => Node | void;
|
||||
/**
|
||||
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
|
||||
*/
|
||||
declare export const findNodeAtOffset: (
|
||||
root: Node,
|
||||
offset: number,
|
||||
includeRightBound?: boolean,
|
||||
) => Node | void;
|
||||
/**
|
||||
* Gets the JSON path of the given JSON DOM node
|
||||
*/
|
||||
declare export const getNodePath: (node: Node) => JSONPath;
|
||||
/**
|
||||
* Evaluates the JavaScript object of the given JSON DOM node
|
||||
*/
|
||||
declare export const getNodeValue: (node: Node) => any;
|
||||
/**
|
||||
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
|
||||
*/
|
||||
declare export const visit: (
|
||||
text: string,
|
||||
visitor: JSONVisitor,
|
||||
options?: ParseOptions,
|
||||
) => any;
|
||||
/**
|
||||
* Takes JSON with JavaScript-style comments and remove
|
||||
* them. Optionally replaces every none-newline character
|
||||
* of comments with a replaceCharacter
|
||||
*/
|
||||
declare export const stripComments: (
|
||||
text: string,
|
||||
replaceCh?: string,
|
||||
) => string;
|
||||
export type ParseError = {
|
||||
error: ParseErrorCode,
|
||||
offset: number,
|
||||
length: number,
|
||||
};
|
||||
export type ParseErrorCode = number;
|
||||
declare export function printParseErrorCode(
|
||||
code: ParseErrorCode,
|
||||
):
|
||||
| 'InvalidSymbol'
|
||||
| 'InvalidNumberFormat'
|
||||
| 'PropertyNameExpected'
|
||||
| 'ValueExpected'
|
||||
| 'ColonExpected'
|
||||
| 'CommaExpected'
|
||||
| 'CloseBraceExpected'
|
||||
| 'CloseBracketExpected'
|
||||
| 'EndOfFileExpected'
|
||||
| 'InvalidCommentToken'
|
||||
| 'UnexpectedEndOfComment'
|
||||
| 'UnexpectedEndOfString'
|
||||
| 'UnexpectedEndOfNumber'
|
||||
| 'InvalidUnicode'
|
||||
| 'InvalidEscapeCharacter'
|
||||
| 'InvalidCharacter'
|
||||
| '<unknown ParseErrorCode>';
|
||||
export type NodeType =
|
||||
| 'object'
|
||||
| 'array'
|
||||
| 'property'
|
||||
| 'string'
|
||||
| 'number'
|
||||
| 'boolean'
|
||||
| 'null';
|
||||
export type Node = {
|
||||
type: NodeType,
|
||||
value?: any,
|
||||
offset: number,
|
||||
length: number,
|
||||
colonOffset?: number,
|
||||
parent?: Node,
|
||||
children?: Node[],
|
||||
};
|
||||
/**
|
||||
* A {@linkcode JSONPath} segment. Either a string representing an object property name
|
||||
* or a number (starting at 0) for array indices.
|
||||
*/
|
||||
export type Segment = string | number;
|
||||
export type JSONPath = Segment[];
|
||||
export type Location = {
|
||||
/**
|
||||
* The previous property key or literal value (string, number, boolean or null) or undefined.
|
||||
*/
|
||||
previousNode?: Node,
|
||||
/**
|
||||
* The path describing the location in the JSON document. The path consists of a sequence of strings
|
||||
* representing an object property or numbers for array indices.
|
||||
*/
|
||||
path: JSONPath,
|
||||
/**
|
||||
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
|
||||
* '*' will match a single segment of any property name or index.
|
||||
* '**' will match a sequence of segments of any property name or index, or no segment.
|
||||
*/
|
||||
matches: (patterns: JSONPath) => boolean,
|
||||
/**
|
||||
* If set, the location's offset is at a property key.
|
||||
*/
|
||||
isAtPropertyKey: boolean,
|
||||
};
|
||||
export type ParseOptions = {
|
||||
disallowComments?: boolean,
|
||||
allowTrailingComma?: boolean,
|
||||
allowEmptyContent?: boolean,
|
||||
};
|
||||
/**
|
||||
* Visitor called by {@linkcode visit} when parsing JSON.
|
||||
*
|
||||
* The visitor functions have the following common parameters:
|
||||
* - `offset`: Global offset within the JSON document, starting at 0
|
||||
* - `startLine`: Line number, starting at 0
|
||||
* - `startCharacter`: Start character (column) within the current line, starting at 0
|
||||
*
|
||||
* Additionally some functions have a `pathSupplier` parameter which can be used to obtain the
|
||||
* current `JSONPath` within the document.
|
||||
*/
|
||||
export type JSONVisitor = {
|
||||
/**
|
||||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
|
||||
*/
|
||||
onObjectBegin?: (
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
pathSupplier: () => JSONPath,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked when a property is encountered. The offset and length represent the location of the property name.
|
||||
* The `JSONPath` created by the `pathSupplier` refers to the enclosing JSON object, it does not include the
|
||||
* property name yet.
|
||||
*/
|
||||
onObjectProperty?: (
|
||||
property: string,
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
pathSupplier: () => JSONPath,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
|
||||
*/
|
||||
onObjectEnd?: (
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
|
||||
*/
|
||||
onArrayBegin?: (
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
pathSupplier: () => JSONPath,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
|
||||
*/
|
||||
onArrayEnd?: (
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
|
||||
*/
|
||||
onLiteralValue?: (
|
||||
value: any,
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
pathSupplier: () => JSONPath,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
|
||||
*/
|
||||
onSeparator?: (
|
||||
character: string,
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
) => void,
|
||||
/**
|
||||
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
|
||||
*/
|
||||
onComment?: (
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
) => void,
|
||||
/**
|
||||
* Invoked on an error.
|
||||
*/
|
||||
onError?: (
|
||||
error: ParseErrorCode,
|
||||
offset: number,
|
||||
length: number,
|
||||
startLine: number,
|
||||
startCharacter: number,
|
||||
) => void,
|
||||
};
|
||||
/**
|
||||
* An edit result describes a textual edit operation. It is the result of a {@linkcode format} and {@linkcode modify} operation.
|
||||
* It consist of one or more edits describing insertions, replacements or removals of text segments.
|
||||
* * The offsets of the edits refer to the original state of the document.
|
||||
* * No two edits change or remove the same range of text in the original document.
|
||||
* * Multiple edits can have the same offset if they are multiple inserts, or an insert followed by a remove or replace.
|
||||
* * The order in the array defines which edit is applied first.
|
||||
* To apply an edit result use {@linkcode applyEdits}.
|
||||
* In general multiple EditResults must not be concatenated because they might impact each other, producing incorrect or malformed JSON data.
|
||||
*/
|
||||
export type EditResult = Edit[];
|
||||
/**
|
||||
* Represents a text modification
|
||||
*/
|
||||
export type Edit = {
|
||||
/**
|
||||
* The start offset of the modification.
|
||||
*/
|
||||
offset: number,
|
||||
/**
|
||||
* The length of the modification. Must not be negative. Empty length represents an *insert*.
|
||||
*/
|
||||
length: number,
|
||||
/**
|
||||
* The new content. Empty content represents a *remove*.
|
||||
*/
|
||||
content: string,
|
||||
};
|
||||
/**
|
||||
* A text range in the document
|
||||
*/
|
||||
export type Range = {
|
||||
/**
|
||||
* The start offset of the range.
|
||||
*/
|
||||
offset: number,
|
||||
/**
|
||||
* The length of the range. Must not be negative.
|
||||
*/
|
||||
length: number,
|
||||
};
|
||||
/**
|
||||
* Options used by {@linkcode format} when computing the formatting edit operations
|
||||
*/
|
||||
export type FormattingOptions = $ReadOnly<{
|
||||
/**
|
||||
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
|
||||
*/
|
||||
tabSize?: number,
|
||||
/**
|
||||
* Is indentation based on spaces?
|
||||
*/
|
||||
insertSpaces?: boolean,
|
||||
/**
|
||||
* The default 'end of line' character. If not set, '\n' is used as default.
|
||||
*/
|
||||
eol?: string,
|
||||
}>;
|
||||
/**
|
||||
* Computes the edit operations needed to format a JSON document.
|
||||
*
|
||||
* @param documentText The input text
|
||||
* @param range The range to format or `undefined` to format the full content
|
||||
* @param options The formatting options
|
||||
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
|
||||
* To apply the edit operations to the input, use {@linkcode applyEdits}.
|
||||
*/
|
||||
declare export function format(
|
||||
documentText: string,
|
||||
range: Range | void,
|
||||
options: FormattingOptions,
|
||||
): EditResult;
|
||||
/**
|
||||
* Options used by {@linkcode modify} when computing the modification edit operations
|
||||
*/
|
||||
export type ModificationOptions = {
|
||||
/**
|
||||
* Formatting options.
|
||||
*/
|
||||
formattingOptions: FormattingOptions,
|
||||
/**
|
||||
* Optional function to define the insertion index given an existing list of properties.
|
||||
*/
|
||||
getInsertionIndex?: (properties: string[]) => number,
|
||||
};
|
||||
/**
|
||||
* Computes the edit operations needed to modify a value in the JSON document.
|
||||
*
|
||||
* @param documentText The input text
|
||||
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
|
||||
* If the path points to an non-existing property or item, it will be created.
|
||||
* @param value The new value for the specified property or item. If the value is undefined,
|
||||
* the property or item will be removed.
|
||||
* @param options Options
|
||||
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
|
||||
* To apply the edit operations to the input, use {@linkcode applyEdits}.
|
||||
*/
|
||||
declare export function modify(
|
||||
text: string,
|
||||
path: JSONPath,
|
||||
value: any,
|
||||
options: ModificationOptions,
|
||||
): EditResult;
|
||||
/**
|
||||
* Applies edits to an input string.
|
||||
* @param text The input text
|
||||
* @param edits Edit operations following the format described in {@linkcode EditResult}.
|
||||
* @returns The text with the applied edits.
|
||||
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
|
||||
*/
|
||||
declare export function applyEdits(text: string, edits: EditResult): string;
|
||||
}
|
||||
@@ -91,19 +91,7 @@ declare module 'tinybench' {
|
||||
beforeEach?: (this: Task) => void | Promise<void>,
|
||||
};
|
||||
|
||||
// This is defined as an interface in tinybench but we define it as an object
|
||||
// to catch problems like `overriddenDuration` being misspelled.
|
||||
export type FnReturnedObject = {
|
||||
overriddenDuration?: number,
|
||||
};
|
||||
|
||||
// This type is defined as returning `unknown` instead of `void` in tinybench,
|
||||
// but we type it this way to avoid mistakes (we can make breaking changes
|
||||
// in our definition that they can't).
|
||||
export type Fn = () =>
|
||||
| Promise<void | FnReturnedObject>
|
||||
| void
|
||||
| FnReturnedObject;
|
||||
export type Fn = () => Promise<mixed> | mixed;
|
||||
|
||||
declare export class Bench extends EventTarget {
|
||||
concurrency: null | 'task' | 'bench';
|
||||
Vendored
+2
-2
@@ -21,7 +21,7 @@ declare type ws$PerMessageDeflateOptions = {
|
||||
maxPayload?: number,
|
||||
};
|
||||
|
||||
/* $FlowFixMe[incompatible-type] - Found with Flow v0.143.1 upgrade
|
||||
/* $FlowFixMe[incompatible-extend] - Found with Flow v0.143.1 upgrade
|
||||
* "on" definition failing with string is incompatible with string literal */
|
||||
declare class ws$WebSocketServer extends events$EventEmitter {
|
||||
/**
|
||||
@@ -141,7 +141,7 @@ declare type ws$UnexpectedResponseListener = (
|
||||
) => mixed;
|
||||
declare type ws$UpgradeListener = (response: http$IncomingMessage<>) => mixed;
|
||||
|
||||
/* $FlowFixMe[incompatible-type] - Found with Flow v0.143.1 upgrade
|
||||
/* $FlowFixMe[incompatible-extend] - Found with Flow v0.143.1 upgrade
|
||||
* "on" definition failing with string is incompatible with string literal */
|
||||
declare class ws$WebSocket extends events$EventEmitter {
|
||||
static Server: typeof ws$WebSocketServer;
|
||||
|
||||
@@ -12,6 +12,3 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
# Controls whether to use Hermes from nightly builds. This will speed up builds
|
||||
# but should NOT be turned on for CI or release builds.
|
||||
react.internal.useHermesNightly=false
|
||||
|
||||
# Controls whether to use Hermes 1.0. Clean and rebuild when changing.
|
||||
hermesV1Enabled=false
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -86,7 +86,7 @@ module.exports = {
|
||||
globalPrefix: '',
|
||||
hermesParser: true,
|
||||
hot: false,
|
||||
// $FlowFixMe[incompatible-type] TODO: Remove when `inlineRequires` has been removed from metro-babel-transformer in OSS
|
||||
// $FlowFixMe[incompatible-call] TODO: Remove when `inlineRequires` has been removed from metro-babel-transformer in OSS
|
||||
inlineRequires: true,
|
||||
minify: false,
|
||||
platform: '',
|
||||
@@ -111,7 +111,7 @@ module.exports = {
|
||||
|
||||
return generate(
|
||||
ast,
|
||||
// $FlowFixMe[incompatible-type] Error found when improving flow typing for libs
|
||||
// $FlowFixMe[prop-missing] Error found when improving flow typing for libs
|
||||
{
|
||||
code: true,
|
||||
comments: false,
|
||||
|
||||
+19
-26
@@ -54,19 +54,16 @@
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-flow": "^7.24.7",
|
||||
"@electron/packager": "^18.3.6",
|
||||
"@expo/spawn-async": "^1.7.2",
|
||||
"@jest/create-cache-key-function": "^29.7.0",
|
||||
"@microsoft/api-extractor": "^7.52.2",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@react-native/metro-babel-transformer": "0.82.0-main",
|
||||
"@react-native/metro-config": "0.82.0-main",
|
||||
"@react-native/metro-babel-transformer": "0.81.0-main",
|
||||
"@react-native/metro-config": "0.81.0-main",
|
||||
"@tsconfig/node22": "22.0.2",
|
||||
"@types/react": "^19.1.0",
|
||||
"@typescript-eslint/parser": "^8.36.0",
|
||||
"ansi-regex": "^5.0.0",
|
||||
"ansi-styles": "^4.2.1",
|
||||
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
|
||||
"babel-plugin-syntax-hermes-parser": "0.32.0",
|
||||
"babel-plugin-syntax-hermes-parser": "0.29.1",
|
||||
"babel-plugin-transform-define": "^2.1.4",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"clang-format": "^1.8.0",
|
||||
@@ -78,51 +75,47 @@
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-ft-flow": "^2.0.1",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"eslint-plugin-redundant-undefined": "^0.4.0",
|
||||
"eslint-plugin-relay": "^1.8.3",
|
||||
"fb-dotslash": "0.5.8",
|
||||
"flow-api-translator": "0.32.0",
|
||||
"flow-bin": "^0.281.0",
|
||||
"flow-api-translator": "0.29.1",
|
||||
"flow-bin": "^0.275.0",
|
||||
"glob": "^7.1.1",
|
||||
"hermes-eslint": "0.32.0",
|
||||
"hermes-transform": "0.32.0",
|
||||
"hermes-eslint": "0.29.1",
|
||||
"hermes-transform": "0.29.1",
|
||||
"ini": "^5.0.0",
|
||||
"inquirer": "^7.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-config": "^29.7.0",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-junit": "^10.0.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
"jsonc-parser": "2.2.1",
|
||||
"markdownlint-cli2": "^0.17.2",
|
||||
"markdownlint-rule-relative-links": "^3.0.0",
|
||||
"memfs": "^4.38.2",
|
||||
"metro-babel-register": "^0.83.1",
|
||||
"metro-transform-plugins": "^0.83.1",
|
||||
"memfs": "^4.7.7",
|
||||
"metro-babel-register": "^0.83.0",
|
||||
"metro-transform-plugins": "^0.83.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"node-fetch": "^2.2.0",
|
||||
"nullthrows": "^1.1.1",
|
||||
"prettier": "3.6.2",
|
||||
"prettier-plugin-hermes-parser": "0.32.0",
|
||||
"react": "19.1.1",
|
||||
"react-test-renderer": "19.1.1",
|
||||
"prettier": "2.8.8",
|
||||
"prettier-plugin-hermes-parser": "0.29.1",
|
||||
"react": "19.1.0",
|
||||
"react-test-renderer": "19.1.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"signedsource": "^2.0.0",
|
||||
"signedsource": "^1.0.0",
|
||||
"supports-color": "^7.1.0",
|
||||
"temp-dir": "^2.0.0",
|
||||
"tinybench": "^4.1.0",
|
||||
"tinybench": "^3.1.0",
|
||||
"typescript": "5.8.3",
|
||||
"ws": "^6.2.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"eslint-plugin-react-hooks": "6.1.0-canary-12bc60f5-20250613",
|
||||
"react-is": "19.1.1",
|
||||
"on-headers": "1.1.0",
|
||||
"compression": "1.8.1"
|
||||
"react-is": "19.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/assets-registry",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "Asset support code for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -81,147 +81,10 @@ export {Commands};
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_INVALID = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
// Coverage instrumentation of invalid Commands export - should still fail
|
||||
export const Commands = (cov_1234567890().s[0]++, {
|
||||
hotspotUpdate: () => {},
|
||||
scrollTo: () => {},
|
||||
});
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_WRONG_FUNCTION = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
// Coverage instrumentation of wrong function call - should fail
|
||||
export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COMPLEX_COVERAGE_INVALID = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
// Complex coverage instrumentation with invalid nested structure - should fail
|
||||
export const Commands = (
|
||||
cov_xyz789().f[1]++,
|
||||
cov_xyz789().s[2]++,
|
||||
{
|
||||
pause: (ref) => {},
|
||||
play: (ref) => {},
|
||||
}
|
||||
);
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_WRONG_NAME = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
// Coverage instrumentation with correct function but wrong export name - should fail
|
||||
export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
// Coverage instrumentation with type cast but wrong function - should fail
|
||||
export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
'CommandsExportedWithDifferentNameNativeComponent.js':
|
||||
COMMANDS_EXPORTED_WITH_DIFFERENT_NAME,
|
||||
'CommandsExportedWithShorthandNativeComponent.js':
|
||||
COMMANDS_EXPORTED_WITH_SHORTHAND,
|
||||
'OtherCommandsExportNativeComponent.js': OTHER_COMMANDS_EXPORT,
|
||||
'CommandsWithCoverageInvalidNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_INVALID,
|
||||
'CommandsWithCoverageWrongFunctionNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_WRONG_FUNCTION,
|
||||
'CommandsWithComplexCoverageInvalidNativeComponent.js':
|
||||
COMMANDS_WITH_COMPLEX_COVERAGE_INVALID,
|
||||
'CommandsWithCoverageWrongNameNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_WRONG_NAME,
|
||||
'CommandsWithCoverageTypeCastInvalidNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID,
|
||||
};
|
||||
|
||||
@@ -59,92 +59,6 @@ export default codegenNativeComponent<ModuleProps>('Module', {
|
||||
});
|
||||
`;
|
||||
|
||||
// Coverage instrumentation test cases - should be recognized as valid
|
||||
const COMMANDS_WITH_SIMPLE_COVERAGE = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
export const Commands = (cov_1234567890.s[0]++, codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module');
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COMPLEX_COVERAGE = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+seek: (viewRef: React.ElementRef<NativeType>, position: number) => void;
|
||||
+stop: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
export const Commands = (
|
||||
cov_abcdef123().f[2]++,
|
||||
cov_abcdef123().s[5]++,
|
||||
codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['seek', 'stop'],
|
||||
})
|
||||
);
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module');
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_TYPE_CAST_COVERAGE = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+mute: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+unmute: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
export const Commands: NativeCommands = (cov_xyz789().s[1]++, codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['mute', 'unmute'],
|
||||
}));
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module');
|
||||
`;
|
||||
|
||||
const FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT = `
|
||||
// @flow
|
||||
|
||||
@@ -193,9 +107,4 @@ module.exports = {
|
||||
'NotANativeComponent.js': NOT_A_NATIVE_COMPONENT,
|
||||
'FullNativeComponent.js': FULL_NATIVE_COMPONENT,
|
||||
'FullTypedNativeComponent.js': FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT,
|
||||
'CommandsWithSimpleCoverageNativeComponent.js': COMMANDS_WITH_SIMPLE_COVERAGE,
|
||||
'CommandsWithComplexCoverageNativeComponent.js':
|
||||
COMMANDS_WITH_COMPLEX_COVERAGE,
|
||||
'CommandsWithTypeCastCoverageNativeComponent.js':
|
||||
COMMANDS_WITH_TYPE_CAST_COVERAGE,
|
||||
};
|
||||
|
||||
@@ -1,77 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for CommandsWithComplexCoverageNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
import type { NativeComponentType } from 'codegenNativeComponent';
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps
|
||||
|}>;
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
interface NativeCommands {
|
||||
+seek: (viewRef: React.ElementRef<NativeType>, position: number) => void,
|
||||
+stop: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
}
|
||||
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
|
||||
let nativeComponentName = 'Module';
|
||||
export const __INTERNAL_VIEW_CONFIG = {
|
||||
uiViewClassName: \\"Module\\",
|
||||
validAttributes: {}
|
||||
};
|
||||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for CommandsWithSimpleCoverageNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
import type { NativeComponentType } from 'codegenNativeComponent';
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps
|
||||
|}>;
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
}
|
||||
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
|
||||
let nativeComponentName = 'Module';
|
||||
export const __INTERNAL_VIEW_CONFIG = {
|
||||
uiViewClassName: \\"Module\\",
|
||||
validAttributes: {}
|
||||
};
|
||||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for CommandsWithTypeCastCoverageNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
import type { NativeComponentType } from 'codegenNativeComponent';
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps
|
||||
|}>;
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
interface NativeCommands {
|
||||
+mute: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
+unmute: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
}
|
||||
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
|
||||
let nativeComponentName = 'Module';
|
||||
export const __INTERNAL_VIEW_CONFIG = {
|
||||
uiViewClassName: \\"Module\\",
|
||||
validAttributes: {}
|
||||
};
|
||||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for FullNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
@@ -225,61 +153,6 @@ exports[`Babel plugin inline view configs fails on inline config for CommandsExp
|
||||
24 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithComplexCoverageInvalidNativeComponent.js 1`] = `
|
||||
"/CommandsWithComplexCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
14 |
|
||||
15 | // Complex coverage instrumentation with invalid nested structure - should fail
|
||||
> 16 | export const Commands = (
|
||||
| ^
|
||||
17 | cov_xyz789().f[1]++,
|
||||
18 | cov_xyz789().s[2]++,
|
||||
19 | {"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageInvalidNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
14 |
|
||||
15 | // Coverage instrumentation of invalid Commands export - should still fail
|
||||
> 16 | export const Commands = (cov_1234567890().s[0]++, {
|
||||
| ^
|
||||
17 | hotspotUpdate: () => {},
|
||||
18 | scrollTo: () => {},
|
||||
19 | });"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageTypeCastInvalidNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageTypeCastInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
19 |
|
||||
20 | // Coverage instrumentation with type cast but wrong function - should fail
|
||||
> 21 | export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({
|
||||
| ^
|
||||
22 | supportedCommands: ['pause', 'play'],
|
||||
23 | }));
|
||||
24 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongFunctionNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageWrongFunctionNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
14 |
|
||||
15 | // Coverage instrumentation of wrong function call - should fail
|
||||
> 16 | export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({
|
||||
| ^
|
||||
17 | supportedCommands: ['pause', 'play'],
|
||||
18 | }));
|
||||
19 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongNameNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageWrongNameNativeComponent.js: Native commands must be exported with the name 'Commands'
|
||||
20 |
|
||||
21 | // Coverage instrumentation with correct function but wrong export name - should fail
|
||||
> 22 | export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands<NativeCommands>({
|
||||
| ^
|
||||
23 | supportedCommands: ['pause', 'play'],
|
||||
24 | }));
|
||||
25 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for OtherCommandsExportNativeComponent.js 1`] = `
|
||||
"/OtherCommandsExportNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
17 | }
|
||||
|
||||
@@ -24,12 +24,12 @@ try {
|
||||
} catch (e) {
|
||||
// Fallback to lib when source doesn't exit (e.g. when installed as a dev dependency)
|
||||
FlowParser =
|
||||
// $FlowFixMe[cannot-resolve-module]
|
||||
// $FlowIgnore[cannot-resolve-module]
|
||||
require('@react-native/codegen/lib/parsers/flow/parser').FlowParser;
|
||||
TypeScriptParser =
|
||||
// $FlowFixMe[cannot-resolve-module]
|
||||
// $FlowIgnore[cannot-resolve-module]
|
||||
require('@react-native/codegen/lib/parsers/typescript/parser').TypeScriptParser;
|
||||
// $FlowFixMe[cannot-resolve-module]
|
||||
// $FlowIgnore[cannot-resolve-module]
|
||||
RNCodegen = require('@react-native/codegen/lib/generators/RNCodegen');
|
||||
}
|
||||
|
||||
@@ -102,58 +102,6 @@ function isCodegenDeclaration(declaration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isCodegenNativeCommandsDeclaration(declaration) {
|
||||
if (!declaration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle direct calls: codegenNativeCommands()
|
||||
if (
|
||||
declaration.type === 'CallExpression' &&
|
||||
declaration.callee &&
|
||||
declaration.callee.type === 'Identifier' &&
|
||||
declaration.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle coverage instrumentation: (cov_xxx().s[0]++, codegenNativeCommands())
|
||||
if (declaration.type === 'SequenceExpression' && declaration.expressions) {
|
||||
// Get the last expression in the sequence (the actual function call)
|
||||
const lastExpression =
|
||||
declaration.expressions[declaration.expressions.length - 1];
|
||||
// Recursively check if the last expression is a valid codegenNativeCommands call
|
||||
return isCodegenNativeCommandsDeclaration(lastExpression);
|
||||
}
|
||||
|
||||
// Handle Flow type casts: (codegenNativeCommands(): NativeCommands)
|
||||
if (
|
||||
(declaration.type === 'TypeCastExpression' ||
|
||||
declaration.type === 'AsExpression') &&
|
||||
declaration.expression &&
|
||||
declaration.expression.type === 'CallExpression' &&
|
||||
declaration.expression.callee &&
|
||||
declaration.expression.callee.type === 'Identifier' &&
|
||||
declaration.expression.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle TypeScript assertions: codegenNativeCommands() as NativeCommands
|
||||
if (
|
||||
declaration.type === 'TSAsExpression' &&
|
||||
declaration.expression &&
|
||||
declaration.expression.type === 'CallExpression' &&
|
||||
declaration.expression.callee &&
|
||||
declaration.expression.callee.type === 'Identifier' &&
|
||||
declaration.expression.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = function ({parse, types: t}) {
|
||||
return {
|
||||
pre(state) {
|
||||
@@ -177,12 +125,12 @@ module.exports = function ({parse, types: t}) {
|
||||
const firstDeclaration = path.node.declaration.declarations[0];
|
||||
|
||||
if (firstDeclaration.type === 'VariableDeclarator') {
|
||||
// Check if this is a valid codegenNativeCommands call, handling type annotations
|
||||
const isValidCommandsExport = isCodegenNativeCommandsDeclaration(
|
||||
firstDeclaration.init,
|
||||
);
|
||||
|
||||
if (isValidCommandsExport) {
|
||||
if (
|
||||
firstDeclaration.init &&
|
||||
firstDeclaration.init.type === 'CallExpression' &&
|
||||
firstDeclaration.init.callee.type === 'Identifier' &&
|
||||
firstDeclaration.init.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
if (
|
||||
firstDeclaration.id.type === 'Identifier' &&
|
||||
firstDeclaration.id.name !== 'Commands'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/babel-plugin-codegen",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "Babel plugin to generate native module and view manager code for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -26,7 +26,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.25.3",
|
||||
"@react-native/codegen": "0.82.0-main"
|
||||
"@react-native/codegen": "0.81.0-main"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/community-cli-plugin",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "Core CLI commands for React Native",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@@ -22,16 +22,16 @@
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@react-native/dev-middleware": "0.82.0-main",
|
||||
"@react-native/dev-middleware": "0.81.0-main",
|
||||
"debug": "^4.4.0",
|
||||
"invariant": "^2.2.4",
|
||||
"metro": "^0.83.1",
|
||||
"metro-config": "^0.83.1",
|
||||
"metro-core": "^0.83.1",
|
||||
"metro": "^0.83.0",
|
||||
"metro-config": "^0.83.0",
|
||||
"metro-core": "^0.83.0",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"metro-resolver": "^0.83.1"
|
||||
"metro-resolver": "^0.83.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-native-community/cli": "*",
|
||||
@@ -40,9 +40,6 @@
|
||||
"peerDependenciesMeta": {
|
||||
"@react-native-community/cli": {
|
||||
"optional": true
|
||||
},
|
||||
"@react-native/metro-config": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -116,7 +116,7 @@ function copyAll(filesToCopy: CopiedFiles) {
|
||||
const src = queue.shift();
|
||||
// $FlowFixMe[incompatible-type]
|
||||
const dest = filesToCopy[src];
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-call]
|
||||
copy(src, dest, copyNext);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,9 +31,9 @@ type MiddlewareReturn = {
|
||||
...
|
||||
};
|
||||
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe
|
||||
const unusedStubWSServer: ws$WebSocketServer = {};
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe
|
||||
const unusedMiddlewareStub: Server = {};
|
||||
|
||||
const communityMiddlewareFallback = {
|
||||
@@ -80,7 +80,7 @@ try {
|
||||
'@react-native-community/cli-server-api',
|
||||
{paths: [communityCliPath]},
|
||||
);
|
||||
// $FlowFixMe[unsupported-syntax] dynamic import
|
||||
// $FlowIgnore[unsupported-syntax] dynamic import
|
||||
communityMiddlewareFallback.createDevServerMiddleware = require(
|
||||
communityCliServerApiPath,
|
||||
).createDevServerMiddleware as CreateDevServerMiddleware;
|
||||
|
||||
@@ -92,14 +92,14 @@ async function runServer(
|
||||
console.info(`Starting dev server on ${devServerUrl}\n`);
|
||||
|
||||
if (args.assetPlugins) {
|
||||
// $FlowFixMe[cannot-write] Assigning to readonly property
|
||||
// $FlowIgnore[cannot-write] Assigning to readonly property
|
||||
metroConfig.transformer.assetPlugins = args.assetPlugins.map(plugin =>
|
||||
require.resolve(plugin),
|
||||
);
|
||||
}
|
||||
// TODO(T214991636): Remove legacy Metro log forwarding
|
||||
if (!args.clientLogs) {
|
||||
// $FlowFixMe[cannot-write] Assigning to readonly property
|
||||
// $FlowIgnore[cannot-write] Assigning to readonly property
|
||||
metroConfig.server.forwardClientLogs = false;
|
||||
}
|
||||
|
||||
@@ -127,8 +127,6 @@ async function runServer(
|
||||
const reporter: Reporter = {
|
||||
update(event: TerminalReportableEvent) {
|
||||
terminalReporter.update(event);
|
||||
/* $FlowFixMe[constant-condition] Error discovered during Constant
|
||||
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
|
||||
if (reportEvent) {
|
||||
reportEvent(event);
|
||||
}
|
||||
@@ -146,7 +144,7 @@ async function runServer(
|
||||
}
|
||||
},
|
||||
};
|
||||
// $FlowFixMe[cannot-write] Assigning to readonly property
|
||||
// $FlowIgnore[cannot-write] Assigning to readonly property
|
||||
metroConfig.reporter = reporter;
|
||||
|
||||
await Metro.runServer(metroConfig, {
|
||||
@@ -175,7 +173,7 @@ function getReporterImpl(
|
||||
try {
|
||||
// First we let require resolve it, so we can require packages in node_modules
|
||||
// as expected. eg: require('my-package/reporter');
|
||||
// $FlowFixMe[unsupported-syntax]
|
||||
// $FlowIgnore[unsupported-syntax]
|
||||
return require(customLogReporterPath);
|
||||
} catch (e) {
|
||||
if (e.code !== 'MODULE_NOT_FOUND') {
|
||||
@@ -183,7 +181,7 @@ function getReporterImpl(
|
||||
}
|
||||
// If that doesn't work, then we next try relative to the cwd, eg:
|
||||
// require('./reporter');
|
||||
// $FlowFixMe[unsupported-syntax]
|
||||
// $FlowIgnore[unsupported-syntax]
|
||||
return require(path.resolve(customLogReporterPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ Diff: ${styleText(['dim', 'underline'], newVersion?.diffUrl ?? 'none')}
|
||||
}
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
function isDiffPurgeEntry(data: Partial<DiffPurge>): data is DiffPurge {
|
||||
return (
|
||||
// $FlowFixMe[incompatible-type-guard]
|
||||
@@ -152,7 +153,7 @@ function buildDiffUrl(oldVersion: string, newVersion: string) {
|
||||
* Returns the most recent React Native version available to upgrade to.
|
||||
*/
|
||||
async function getLatestRnDiffPurgeVersion(): Promise<LatestVersions | void> {
|
||||
const options: RequestOptions = {
|
||||
const options = {
|
||||
// https://developer.github.com/v3/#user-agent-required
|
||||
headers: {'User-Agent': '@react-native/community-cli-plugin'} as Headers,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/core-cli-utils",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "React Native CLI library for Frameworks to build on",
|
||||
"license": "MIT",
|
||||
"main": "./src/index.flow.js",
|
||||
|
||||
@@ -71,7 +71,7 @@ const FIRST = 1,
|
||||
FOURTH = 4;
|
||||
|
||||
function getNodePackagePath(packageName: string): string {
|
||||
// $FlowFixMe[prop-missing] type definition is incomplete
|
||||
// $FlowIgnore[prop-missing] type definition is incomplete
|
||||
return require.resolve(packageName, {cwd: [process.cwd(), ...module.paths]});
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ const FIRST = 1,
|
||||
FIFTH = 5;
|
||||
|
||||
function getNodePackagePath(packageName: string): string {
|
||||
// $FlowFixMe[prop-missing] type definition is incomplete
|
||||
// $FlowIgnore[prop-missing] type definition is incomplete
|
||||
return require.resolve(packageName, {cwd: [process.cwd(), ...module.paths]});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@generated SignedSource<<0b54f75686e4893a5444839bf621a317>>
|
||||
Git revision: 5a792db1225adda206313e9bf751198a1ca7851a
|
||||
@generated SignedSource<<e2d97b04634bf3b566f33d455f38658f>>
|
||||
Git revision: 8dc0d5b365e6b4600b78e5a7381aa861d8c1c81e
|
||||
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
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -238,7 +238,7 @@ import"../../ui/components/icon_button/icon_button.js";import"../../ui/component
|
||||
<td>${e.method}</td>
|
||||
<td>${e.params?d`<code>${JSON.stringify(e.params)}</code>`:""}</td>
|
||||
<td>
|
||||
${e.result?d`<code>${JSON.stringify(e.result)}</code>`:e.error?d`<code>${JSON.stringify(e.error)}</code>`:"id"in e?"(pending)":""}
|
||||
${e.result?d`<code>${JSON.stringify(e.result)}</code>`:e.error?d`<code>${JSON.stringify(e.error)}</code>`:"(pending)"}
|
||||
</td>
|
||||
<td data-value=${e.elapsedTime||0}>
|
||||
${"id"in e?e.elapsedTime?U(A.sMs,{PH1:String(e.elapsedTime)}):"(pending)":""}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/debugger-frontend",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "Debugger frontend for React Native based on Chrome DevTools",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile fails with the expected error message for a missing dotslash file 1`] = `
|
||||
Object {
|
||||
"code": "unexpected_error",
|
||||
"humanReadableMessage": "An unexpected error occured while installing the latest version of React Native DevTools. Using a fallback version instead.",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile fails with the expected error message for missing platforms 1`] = `
|
||||
Object {
|
||||
"code": "platform_not_supported",
|
||||
"humanReadableMessage": "The latest version of React Native DevTools is not supported on this platform. Using a fallback version instead.",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile scenarios requiring a local HTTP server fails with the expected error message for a corrupted tarball 1`] = `
|
||||
Object {
|
||||
"code": "possible_corruption",
|
||||
"humanReadableMessage": "Failed to verify the latest version of React Native DevTools. Using a fallback version instead. ",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile scenarios requiring a local HTTP server fails with the expected error message for a network error 1`] = `
|
||||
Object {
|
||||
"code": "likely_offline",
|
||||
"humanReadableMessage": "Failed to download the latest version of React Native DevTools. Using a fallback version instead. Connect to the internet or check your network settings.",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 113510892,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 113243910,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 108810433,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 113769989,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 113510892,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 113243910,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 108810433,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 113769989,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {}
|
||||
}
|
||||
@@ -1,139 +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
|
||||
*/
|
||||
|
||||
const {
|
||||
prepareDebuggerShellFromDotSlashFile,
|
||||
} = require('../src/node/private/LaunchUtils');
|
||||
const fs = require('fs').promises;
|
||||
const http = require('http');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
// The implementation of prepareDebuggerShellFromDotSlashFile relies on
|
||||
// details of DotSlash that are not guaranteed to be stable (support for
|
||||
// `dotslash -- fetch <file>`, certain strings being printed to stderr).
|
||||
// This (admittedly elaborate) test suite ensures we'll fail loudly if we
|
||||
// try to upgrade DotSlash to a version that breaks our assumptions.
|
||||
describe('prepareDebuggerShellFromDotSlashFile', () => {
|
||||
test('fails with the expected error message for missing platforms', async () => {
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(__dirname, 'dotslash-file-with-missing-platforms.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
test('fails with the expected error message for a missing dotslash file', async () => {
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(__dirname, 'dotslash-file-that-does-not-exist.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenarios requiring a local HTTP server', () => {
|
||||
let server, scratchDir;
|
||||
|
||||
beforeEach(async () => {
|
||||
scratchDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dotslash-test-'));
|
||||
server = http.createServer((request, response) => {
|
||||
if (request.url === '/corrupted.tar.gz') {
|
||||
response.writeHead(200, {'Content-Type': 'application/gzip'});
|
||||
response.end(
|
||||
'Hello, world!\n' + 'This simulated a corrupted tarball.',
|
||||
);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
await new Promise((resolve, reject) => {
|
||||
server.on('error', reject);
|
||||
server.listen(0, 'localhost', () => {
|
||||
server.removeListener('error', reject);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(scratchDir, {recursive: true, force: true});
|
||||
if (server.listening) {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close(error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('fails with the expected error message for a corrupted tarball', async () => {
|
||||
const dotslashFileContents = injectHostPort(
|
||||
await fs.readFile(
|
||||
path.join(
|
||||
__dirname,
|
||||
'dotslash-file-simulating-data-corruption.jsonc',
|
||||
),
|
||||
'utf8',
|
||||
),
|
||||
server.address(),
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
dotslashFileContents,
|
||||
);
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
test('fails with the expected error message for a network error', async () => {
|
||||
const dotslashFileContents = injectHostPort(
|
||||
await fs.readFile(
|
||||
path.join(__dirname, 'dotslash-file-simulating-network-error.jsonc'),
|
||||
'utf8',
|
||||
),
|
||||
server.address(),
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
dotslashFileContents,
|
||||
);
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function injectHostPort(
|
||||
dotslashFileContents: string,
|
||||
address: net$Socket$address,
|
||||
) {
|
||||
const host =
|
||||
address.family === 'IPv6' ? `[${address.address}]` : address.address;
|
||||
return dotslashFileContents
|
||||
.replaceAll('$HOST', host)
|
||||
.replaceAll('$PORT', address.port.toString());
|
||||
}
|
||||
@@ -19,13 +19,13 @@ const semver = require('semver');
|
||||
// safety to ensure the target of the resolution is in sync with the declared dependency.
|
||||
describe('Electron dependency', () => {
|
||||
test('should be semver-satisfied by the actual electron version', () => {
|
||||
// $FlowFixMe[untyped-import] - package.json is not typed
|
||||
// $FlowIssue[untyped-import] - package.json is not typed
|
||||
const ourPackageJson = require('../package.json');
|
||||
|
||||
const declaredElectronVersion = ourPackageJson.devDependencies.electron;
|
||||
const declaredElectronVersion = ourPackageJson.dependencies.electron;
|
||||
expect(declaredElectronVersion).toBeTruthy();
|
||||
|
||||
// $FlowFixMe[untyped-import] - package.json is not typed
|
||||
// $FlowIssue[untyped-import] - package.json is not typed
|
||||
const electronPackageJson = require('electron/package.json');
|
||||
|
||||
const actualElectronVersion = electronPackageJson.version;
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
// @generated SignedSource<<e93d55b5e28943e44271f5d3738083e0>>
|
||||
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 116060647,
|
||||
"hash": "sha256",
|
||||
"digest": "4352f1c9848ca919101ec628bd08b87a72a828d1ab55fa43a02098329fa452fa",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQM24LW0nPZRk0ypuIG9prz_72YjBJNcVlGSIEpO4zdlLXgw4dFNodH7MKg9eKNTnx7wrVDDBNACrnEPt_OfXOjZyZsV9Oaqu0-vNFRdlEyis4YqmpqGLtz3LvD-9R6fzcWxJI9zrhdPvOvlXP-3Syt1UNITaxXDVqIwAAYpCaAh0oLpupAFCc2yvYw"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 115930333,
|
||||
"hash": "sha256",
|
||||
"digest": "11c7b07942928a6301b07fbf2bc77ce1229b2a52891f23541cdd9858b5250e64",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQNDwm7HZRhtNxHqMr1FfSb0afHFGrn1OHxH0gOiggrLrht9QRUgJ3GG5jj7huhQzMRogE-LCMsnxh1ioOZks-YYX4KRt6Kj1-whdWsGFc7lBhPOpk1ssbYFGN1NNyuyFRmH-3nCY3lBC4AmbCUkbDTUeCi9DidCtJeyc73CZJEu7M62rIzxR2yV"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 110891041,
|
||||
"hash": "sha256",
|
||||
"digest": "3cbe8b1b3d17e433347f1601435bb9a6cb758528a5c176a66fc52d9977223175",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOmC2cqqSv4OrSJKJroYVg_NE8OE4O73AXqY7wXiYqiWQVkDt0Xnyw3ZeUpQT_Qb0-OoT5F8REKoFrB6eqwat8Ovkyina30peYTTwNUzmwnnGQEg7J0fOHNxLF4dkmU1FagXtsoWgex4dKgsK_VpcMsHj3Vp7diomkYvWBVTf_gPVEseYSN9oKq92qa"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 117766158,
|
||||
"hash": "sha256",
|
||||
"digest": "6fb79bc2ba3008401b4c9c128248657b95b98581ccde60f8fadb622163779775",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQMMtGn-YGdfLfTVWC8zbQkQx65Asq6iArKt1t__cjZ8UY_s6-sX5XBHr8k1SaexAO21dFZENQVZ1jW_wn_gJ9ENvosQDG1KfWMViKsHli0xRzZ1HVsgPIj_KVXe907QZwwtJf2XhgH0HT8dfH-AQdDcd0_TB5DFUwOsHzhH0nBrHet7YFkbJtPTaA"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"size": 125527537,
|
||||
"hash": "sha256",
|
||||
"digest": "579a5b0944c51c3b1b541ad5af66c1ffedf93cae2a891ecdf88cb7219fd9b096",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOQ3E8lBXqdVHbDPyVb5AOQMrDWjFrFV8fnLLBsygQvLWdpu6ixyG9PgWdwpi5jM-XcDdCHkhBdhaq-5dwT_tgRWKCAMsEBoAIUk0Xg77mGyHG2VF7bNfQ2qFBMuObrsTmrKy1nJ-UFDDm29pJD4GkFQW5NesiBwndJj8t3B8Ur8cczh_XR8rF5"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-win32-x64/React Native DevTools.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/debugger-shell",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "Experimental debugger shell for React Native for use with @react-native/debugger-frontend",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@@ -26,20 +26,14 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20.19.4"
|
||||
"node": ">= 20.19.4",
|
||||
"electron": ">=36.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"fb-dotslash": "0.5.8"
|
||||
"electron": "36.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "37.2.6",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
"files": [
|
||||
"!**/__tests__/**",
|
||||
"bin",
|
||||
"dist",
|
||||
"!src/electron"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function handleLaunchArgs(argv: string[]) {
|
||||
});
|
||||
|
||||
// Find an existing window for this app and launch configuration.
|
||||
let frontendWindow = BrowserWindow.getAllWindows().find(window => {
|
||||
const existingWindow = BrowserWindow.getAllWindows().find(window => {
|
||||
const metadata = windowMetadata.get(window);
|
||||
if (!metadata) {
|
||||
return false;
|
||||
@@ -44,39 +44,41 @@ function handleLaunchArgs(argv: string[]) {
|
||||
return metadata.windowKey === windowKey;
|
||||
});
|
||||
|
||||
if (frontendWindow) {
|
||||
if (existingWindow) {
|
||||
// If the window is already visible, flash it.
|
||||
if (frontendWindow.isVisible()) {
|
||||
frontendWindow.flashFrame(true);
|
||||
if (existingWindow.isVisible()) {
|
||||
existingWindow.flashFrame(true);
|
||||
setTimeout(() => {
|
||||
frontendWindow.flashFrame(false);
|
||||
existingWindow.flashFrame(false);
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
// Create the browser window.
|
||||
frontendWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
partition: 'persist:react-native-devtools',
|
||||
preload: require.resolve('./preload.js'),
|
||||
},
|
||||
// Icon for Linux
|
||||
icon: path.join(__dirname, 'resources', 'icon.png'),
|
||||
});
|
||||
// Auto-hide the Windows/Linux menu bar
|
||||
frontendWindow.setMenuBarVisibility(false);
|
||||
if (process.platform === 'darwin') {
|
||||
app.focus({
|
||||
steal: true,
|
||||
});
|
||||
}
|
||||
existingWindow.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the browser window.
|
||||
const frontendWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
partition: 'persist:react-native-devtools',
|
||||
preload: require.resolve('./preload.js'),
|
||||
},
|
||||
// Icon for Linux
|
||||
icon: path.join(__dirname, 'resources', 'icon.png'),
|
||||
});
|
||||
|
||||
// Open links in the default browser instead of in new Electron windows.
|
||||
frontendWindow.webContents.setWindowOpenHandler(({url}) => {
|
||||
shell.openExternal(url);
|
||||
return {action: 'deny'};
|
||||
});
|
||||
|
||||
// TODO: If the window contains a live, working frontend instance with a valid connection to the backend,
|
||||
// we should avoid this reload and instead send the frontend a message to handle the launch arguments
|
||||
// dynamically (e.g. update the launch ID for telemetry purposes, handle deeplinking to a specific CDT panel, etc).
|
||||
frontendWindow.loadURL(frontendUrl);
|
||||
|
||||
windowMetadata.set(frontendWindow, {
|
||||
@@ -88,7 +90,6 @@ function handleLaunchArgs(argv: string[]) {
|
||||
steal: true,
|
||||
});
|
||||
}
|
||||
frontendWindow.focus();
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
|
||||
@@ -8,18 +8,9 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import buildInfo from './BuildInfo';
|
||||
|
||||
// $FlowFixMe[untyped-import] Flow doesn't infer JSON types
|
||||
const pkg = require('../../package.json');
|
||||
const util = require('util');
|
||||
// $FlowFixMe[unclear-type] We have no Flow types for the Electron API.
|
||||
const {app} = require('electron') as any;
|
||||
|
||||
// Set the app name and version early - these are used in --version as well as
|
||||
// in the User-Agent string.
|
||||
app.setName(pkg.name);
|
||||
app.setVersion(pkg.version + '-' + buildInfo.revision);
|
||||
const util = require('util');
|
||||
|
||||
// Handle global command line arguments which don't require a window
|
||||
// or the single instance lock to be held.
|
||||
|
||||
@@ -16,7 +16,7 @@ contextBridge.executeInMainWorld({
|
||||
let didDecorateInspectorFrontendHostInstance = false;
|
||||
// reactNativeDecorateInspectorFrontendHostInstance was introduced in
|
||||
// https://github.com/facebook/react-native-devtools-frontend/pull/168
|
||||
// $FlowFixMe[prop-missing]
|
||||
// $FlowIgnore[prop-missing]
|
||||
globalThis.reactNativeDecorateInspectorFrontendHostInstance = (
|
||||
InspectorFrontendHostInstance: $FlowFixMe,
|
||||
) => {
|
||||
|
||||
@@ -18,9 +18,9 @@ declare module.exports: typeof Node;
|
||||
// Because Electron doesn't support package.json `exports`, we need to
|
||||
// switch at runtime.
|
||||
if ('electron' in process.versions) {
|
||||
// $FlowFixMe[invalid-export]
|
||||
// $FlowIgnore[invalid-export]
|
||||
module.exports = require('./electron');
|
||||
} else {
|
||||
// $FlowFixMe[invalid-export]
|
||||
// $FlowIgnore[invalid-export]
|
||||
module.exports = require('./node');
|
||||
}
|
||||
|
||||
@@ -1,48 +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
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
const {unstable_spawnDebuggerShellWithArgs} = require('../../');
|
||||
|
||||
describe('debugger-shell Node package', () => {
|
||||
test('can spawn in detached+prebuilt mode without crashing', async () => {
|
||||
await expect(
|
||||
unstable_spawnDebuggerShellWithArgs(['--version'], {
|
||||
flavor: 'prebuilt',
|
||||
mode: 'detached',
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// When running in the internal react-native-oss-js job, Electron isn't
|
||||
// installed correctly (postinstall scripts don't run) but the internal
|
||||
// `electron` workspace isn't available either. Detecting this dynamically
|
||||
// weakens the test somewhat in environments where it *should* pass, but this
|
||||
// is a dev-only feature anyway so this is fine.
|
||||
if (isElectronInstalled()) {
|
||||
test('can spawn in detached+dev mode without crashing', async () => {
|
||||
await expect(
|
||||
unstable_spawnDebuggerShellWithArgs(['--version'], {
|
||||
flavor: 'dev',
|
||||
mode: 'detached',
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function isElectronInstalled() {
|
||||
try {
|
||||
require('electron');
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -8,54 +8,44 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
prepareDebuggerShellFromDotSlashFile,
|
||||
spawnAndGetStderr,
|
||||
} from './private/LaunchUtils';
|
||||
|
||||
const {spawn} = require('cross-spawn');
|
||||
const path = require('path');
|
||||
|
||||
// The 'prebuilt' flavor will use the prebuilt shell binary (and the JavaScript embedded in it).
|
||||
// The 'dev' flavor will use a stock Electron binary and run the shell code from the `electron/` directory.
|
||||
type DebuggerShellFlavor = 'prebuilt' | 'dev';
|
||||
|
||||
const DEVTOOLS_BINARY_DOTSLASH_FILE = path.join(
|
||||
__dirname,
|
||||
'../../bin/react-native-devtools',
|
||||
);
|
||||
|
||||
async function unstable_spawnDebuggerShellWithArgs(
|
||||
args: string[],
|
||||
{
|
||||
mode = 'detached',
|
||||
flavor = 'prebuilt',
|
||||
}: $ReadOnly<{
|
||||
// In 'syncAndExit' mode, the current process will block until the spawned process exits, and then it will exit
|
||||
// with the same exit code as the spawned process.
|
||||
// In 'detached' mode, the spawned process will be detached from the current process and the current process will
|
||||
// continue to run normally.
|
||||
mode?: 'syncThenExit' | 'detached',
|
||||
flavor?: DebuggerShellFlavor,
|
||||
}> = {},
|
||||
): Promise<void> {
|
||||
const [binaryPath, baseArgs] = getShellBinaryAndArgs(flavor);
|
||||
// NOTE: Internally at Meta, this is aliased to a workspace that is
|
||||
// API-compatible with the 'electron' package, but contains prebuilt binaries
|
||||
// that do not need to be downloaded in a postinstall action.
|
||||
const electronPath = require('electron');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(binaryPath, [...baseArgs, ...args], {
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
detached: mode === 'detached',
|
||||
});
|
||||
const child = spawn(
|
||||
electronPath,
|
||||
[require.resolve('../electron'), ...args],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
detached: mode === 'detached',
|
||||
},
|
||||
);
|
||||
if (mode === 'detached') {
|
||||
child.on('spawn', () => {
|
||||
resolve();
|
||||
});
|
||||
child.on('close', (code: number) => {
|
||||
child.on('close', (code /*: number */) => {
|
||||
if (code !== 0) {
|
||||
reject(
|
||||
new Error(
|
||||
`Failed to open debugger shell: exited with code ${code}`,
|
||||
`Failed to open debugger shell: ${electronPath} exited with code ${code}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -64,7 +54,7 @@ async function unstable_spawnDebuggerShellWithArgs(
|
||||
} else if (mode === 'syncThenExit') {
|
||||
child.on('close', function (code, signal) {
|
||||
if (code === null) {
|
||||
console.error('Debugger shell exited with signal', signal);
|
||||
console.error(electronPath, 'exited with signal', signal);
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(code);
|
||||
@@ -84,87 +74,4 @@ async function unstable_spawnDebuggerShellWithArgs(
|
||||
});
|
||||
}
|
||||
|
||||
export type DebuggerShellPreparationResult = $ReadOnly<{
|
||||
code:
|
||||
| 'success'
|
||||
| 'not_implemented'
|
||||
| 'likely_offline'
|
||||
| 'platform_not_supported'
|
||||
| 'possible_corruption'
|
||||
| 'unexpected_error',
|
||||
humanReadableMessage?: string,
|
||||
verboseInfo?: string,
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Attempts to prepare the debugger shell for use and returns a coded result
|
||||
* that can be used to advise the user on how to proceed in case of failure.
|
||||
* In particular, this function will attempt to download and extract an
|
||||
* appropriate binary for the "prebuilt" flavor.
|
||||
*
|
||||
* This function should be called early during dev server startup, in parallel
|
||||
* with other initialization steps, so that the debugger shell is ready to use
|
||||
* instantly when the user tries to open it (and conversely, the user is
|
||||
* informed ASAP if it is not ready to use).
|
||||
*/
|
||||
async function unstable_prepareDebuggerShell(
|
||||
flavor: DebuggerShellFlavor,
|
||||
): Promise<DebuggerShellPreparationResult> {
|
||||
const [binaryPath, baseArgs] = getShellBinaryAndArgs(flavor);
|
||||
|
||||
try {
|
||||
switch (flavor) {
|
||||
case 'prebuilt':
|
||||
const prebuiltResult = await prepareDebuggerShellFromDotSlashFile(
|
||||
DEVTOOLS_BINARY_DOTSLASH_FILE,
|
||||
);
|
||||
if (prebuiltResult.code !== 'success') {
|
||||
return prebuiltResult;
|
||||
}
|
||||
break;
|
||||
case 'dev':
|
||||
break;
|
||||
default:
|
||||
flavor as empty;
|
||||
throw new Error(`Unknown flavor: ${flavor}`);
|
||||
}
|
||||
const {code, stderr} = await spawnAndGetStderr(binaryPath, [
|
||||
...baseArgs,
|
||||
'--version',
|
||||
]);
|
||||
if (code !== 0) {
|
||||
return {
|
||||
code: 'unexpected_error',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
return {code: 'success'};
|
||||
} catch (e) {
|
||||
return {
|
||||
code: 'unexpected_error',
|
||||
verboseInfo: e.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getShellBinaryAndArgs(
|
||||
flavor: DebuggerShellFlavor,
|
||||
): [string, Array<string>] {
|
||||
switch (flavor) {
|
||||
case 'prebuilt':
|
||||
return [require('fb-dotslash'), [DEVTOOLS_BINARY_DOTSLASH_FILE]];
|
||||
case 'dev':
|
||||
return [
|
||||
// NOTE: Internally at Meta, this is aliased to a workspace that is
|
||||
// API-compatible with the 'electron' package, but contains prebuilt binaries
|
||||
// that do not need to be downloaded in a postinstall action.
|
||||
require('electron'),
|
||||
[require.resolve('../electron')],
|
||||
];
|
||||
default:
|
||||
flavor as empty;
|
||||
throw new Error(`Unknown flavor: ${flavor}`);
|
||||
}
|
||||
}
|
||||
|
||||
export {unstable_spawnDebuggerShellWithArgs, unstable_prepareDebuggerShell};
|
||||
export {unstable_spawnDebuggerShellWithArgs};
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {DebuggerShellPreparationResult} from '../';
|
||||
|
||||
const {spawn} = require('cross-spawn');
|
||||
|
||||
async function spawnAndGetStderr(
|
||||
command: string,
|
||||
args: string[],
|
||||
): Promise<{
|
||||
code: number,
|
||||
stderr: string,
|
||||
}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(command, args, {
|
||||
stdio: ['ignore', 'ignore', 'pipe'],
|
||||
encoding: 'utf8',
|
||||
windowsHide: true,
|
||||
});
|
||||
let stderr = '';
|
||||
child.stderr.on('data', data => {
|
||||
stderr += data;
|
||||
});
|
||||
child.on('error', error => {
|
||||
reject(error);
|
||||
});
|
||||
child.on('close', (code, signal) => {
|
||||
resolve({
|
||||
code,
|
||||
stderr,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function prepareDebuggerShellFromDotSlashFile(
|
||||
filePath: string,
|
||||
): Promise<DebuggerShellPreparationResult> {
|
||||
const {code, stderr} = await spawnAndGetStderr(require('fb-dotslash'), [
|
||||
'--',
|
||||
'fetch',
|
||||
filePath,
|
||||
]);
|
||||
if (code === 0) {
|
||||
return {code: 'success'};
|
||||
}
|
||||
if (
|
||||
stderr.includes('dotslash error') &&
|
||||
stderr.includes('no providers succeeded')
|
||||
) {
|
||||
if (stderr.includes('failed to verify artifact')) {
|
||||
return {
|
||||
code: 'possible_corruption',
|
||||
humanReadableMessage:
|
||||
'Failed to verify the latest version of React Native DevTools. ' +
|
||||
'Using a fallback version instead. ',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
return {
|
||||
code: 'likely_offline',
|
||||
humanReadableMessage:
|
||||
'Failed to download the latest version of React Native DevTools. ' +
|
||||
'Using a fallback version instead. ' +
|
||||
'Connect to the internet or check your network settings.',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
if (
|
||||
stderr.includes('dotslash error') &&
|
||||
stderr.includes('platform not supported')
|
||||
) {
|
||||
return {
|
||||
code: 'platform_not_supported',
|
||||
humanReadableMessage:
|
||||
'The latest version of React Native DevTools is not supported on this platform. ' +
|
||||
'Using a fallback version instead.',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
return {
|
||||
code: 'unexpected_error',
|
||||
humanReadableMessage:
|
||||
'An unexpected error occured while installing the latest version of React Native DevTools. ' +
|
||||
'Using a fallback version instead.',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
|
||||
export {spawnAndGetStderr, prepareDebuggerShellFromDotSlashFile};
|
||||
@@ -88,16 +88,6 @@ WebSocket handler for registering device connections.
|
||||
|
||||
WebSocket handler that proxies CDP messages to/from the corresponding device.
|
||||
|
||||
## Experimental features
|
||||
|
||||
React Native frameworks may pass an `unstable_experiments` option to `createDevMiddleware` to configure experimental features. Note that these features might not work correctly, and they may change or be removed in the future without notice. Some of the experiment flags available are documented below.
|
||||
|
||||
### `unstable_experiments.enableStandaloneFuseboxShell`
|
||||
|
||||
When `true`, the debugger frontend will launch in a standalone app shell (provided by the `@react-native/debugger-shell` package) rather than in a browser window. The standalone shell provides an improved experience and will become the default in a future version of React Native.
|
||||
|
||||
The shell is powered by a separate binary that is downloaded and cached in the background (immediately after the call to `createDevMiddleware`). If there is a problem downloading or invoking this binary for the first time, the debugger frontend will revert to launching in a browser window until the next time `createDevMiddleware` is called (typically, on the next dev server start).
|
||||
|
||||
## Contributing
|
||||
|
||||
Changes to this package can be made locally and tested against the `rn-tester` app, per the [Contributing guide](https://reactnative.dev/contributing/overview#contributing-code). During development, this package is automatically run from source with no build step.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/dev-middleware",
|
||||
"version": "0.82.0-main",
|
||||
"version": "0.81.0-main",
|
||||
"description": "Dev server middleware for React Native",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@@ -23,8 +23,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@isaacs/ttlcache": "^1.4.1",
|
||||
"@react-native/debugger-frontend": "0.82.0-main",
|
||||
"@react-native/debugger-shell": "0.82.0-main",
|
||||
"@react-native/debugger-frontend": "0.81.0-main",
|
||||
"chrome-launcher": "^0.15.2",
|
||||
"chromium-edge-launcher": "^0.2.0",
|
||||
"connect": "^3.6.5",
|
||||
@@ -39,9 +38,8 @@
|
||||
"node": ">= 20.19.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native/debugger-shell": "0.82.0-main",
|
||||
"selfsigned": "^2.4.1",
|
||||
"undici": "^5.29.0",
|
||||
"undici": "^5.28.5",
|
||||
"wait-for-expect": "^3.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export class DebuggerAgent {
|
||||
this.#ws = null;
|
||||
}
|
||||
|
||||
// $FlowFixMe[unsafe-getters-setters]
|
||||
// $FlowIgnore[unsafe-getters-setters]
|
||||
get socket(): WebSocket {
|
||||
return nullthrows(this.#ws);
|
||||
}
|
||||
@@ -103,12 +103,11 @@ export class DebuggerMock extends DebuggerAgent {
|
||||
originalHandleCallsArray === this.handle.mock.calls
|
||||
? this.handle.mock.calls.slice(originalHandleCallCount)
|
||||
: this.handle.mock.calls;
|
||||
// $FlowFixMe[incompatible-use]
|
||||
// $FlowFixMe[prop-missing]
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowIgnore[incompatible-use]
|
||||
// $FlowIgnore[prop-missing]
|
||||
const [response] = newHandleCalls.find(args => args[0].id === message.id);
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-indexer]
|
||||
// $FlowIgnore[incompatible-return]
|
||||
// $FlowIgnore[incompatible-indexer]
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export class DeviceAgent {
|
||||
});
|
||||
}
|
||||
|
||||
// $FlowFixMe[unsafe-getters-setters]
|
||||
// $FlowIgnore[unsafe-getters-setters]
|
||||
get socket(): WebSocket {
|
||||
return nullthrows(this.#ws);
|
||||
}
|
||||
@@ -152,12 +152,12 @@ export class DeviceMock extends DeviceAgent {
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
// $FlowFixMe[incompatible-type] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
// $FlowFixMe[incompatible-call] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
this.send({event, payload});
|
||||
});
|
||||
return;
|
||||
}
|
||||
// $FlowFixMe[incompatible-type] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
// $FlowFixMe[incompatible-call] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
this.send({event, payload: maybePayload});
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user