mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
115
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40feba6a81 | ||
|
|
44159e35ce | ||
|
|
42eebafb81 | ||
|
|
4e9196d433 | ||
|
|
57ed1bde01 | ||
|
|
454e576b5b | ||
|
|
d0dd48b4c9 | ||
|
|
0e5dc51bba | ||
|
|
82643e1a35 | ||
|
|
6abca78895 | ||
|
|
4f50089da2 | ||
|
|
49bb2f37f1 | ||
|
|
dc851b6b3a | ||
|
|
9a286d65d1 | ||
|
|
1dc11cb2b3 | ||
|
|
5d63c85ced | ||
|
|
462fbaef0c | ||
|
|
d85f8b7e7a | ||
|
|
a37ba6f3ff | ||
|
|
f4b5bf1e14 | ||
|
|
03237f449a | ||
|
|
d81065a621 | ||
|
|
c0c3ac3f13 | ||
|
|
cc6e734fa4 | ||
|
|
f6757a20c6 | ||
|
|
0e5ba6db2d | ||
|
|
36d64312c8 | ||
|
|
4fe26e3b2f | ||
|
|
8bf509ec44 | ||
|
|
76463789fd | ||
|
|
dbc83220e0 | ||
|
|
5eea4cda1b | ||
|
|
0f4234f450 | ||
|
|
396d444e79 | ||
|
|
20bcf57ee3 | ||
|
|
33e3ec9426 | ||
|
|
35f6000c66 | ||
|
|
1d4b263d3d | ||
|
|
2c62dcabe5 | ||
|
|
eed035a3b6 | ||
|
|
f3b9226ca0 | ||
|
|
0a7dbfd1a2 | ||
|
|
d66bb7f853 | ||
|
|
8b5d650e60 | ||
|
|
32deb27219 | ||
|
|
24b1da16ea | ||
|
|
907bd438ff | ||
|
|
eadf9203bf | ||
|
|
8535f496d2 | ||
|
|
d6ee80959e | ||
|
|
bdb1034e3e | ||
|
|
daf012a958 | ||
|
|
40a4df0637 | ||
|
|
ff5d87db3e | ||
|
|
4e677f021a | ||
|
|
42e62bf963 | ||
|
|
3b170a0910 | ||
|
|
680bfafcc2 | ||
|
|
ffb93a81b5 | ||
|
|
9048c0e0b3 | ||
|
|
bccedff09b | ||
|
|
3e5dd5e2f2 | ||
|
|
8a04e4418a | ||
|
|
ec48cb6560 | ||
|
|
768cf6e79c | ||
|
|
bf7f0d57cc | ||
|
|
0e0838448a | ||
|
|
0f4aedfe82 | ||
|
|
407be885fa | ||
|
|
aa0670b0ae | ||
|
|
8830ed6bba | ||
|
|
bc745cb6ff | ||
|
|
24d3251d3f | ||
|
|
57cc8e7f67 | ||
|
|
8a7c718ab3 | ||
|
|
4efab59a1e | ||
|
|
e3da9b8c0a | ||
|
|
7395765cf8 | ||
|
|
66b1cfee97 | ||
|
|
6440e35bc7 | ||
|
|
10d65924ee | ||
|
|
31b62ab7f0 | ||
|
|
1151ea1247 | ||
|
|
a9ec4203e1 | ||
|
|
bd39897abf | ||
|
|
7c2fb7911a | ||
|
|
4dc9d54447 | ||
|
|
27b54bdcff | ||
|
|
c68669079a | ||
|
|
1c1b03d6e8 | ||
|
|
c7e704a0ef | ||
|
|
02d38e4eb5 | ||
|
|
e063a1b0fb | ||
|
|
c09e4dacff | ||
|
|
011cb3a8d7 | ||
|
|
a5b5f6be78 | ||
|
|
29b34e70e3 | ||
|
|
6294453980 | ||
|
|
7822a7796f | ||
|
|
1ccc7a3e9e | ||
|
|
987c1f2880 | ||
|
|
f6f3b5cb98 | ||
|
|
f7644be6d4 | ||
|
|
ad3f6b5274 | ||
|
|
ba3cf235f5 | ||
|
|
d032e35f86 | ||
|
|
b33b80f51c | ||
|
|
b3300d77ea | ||
|
|
fde94ce307 | ||
|
|
1f7ed063c9 | ||
|
|
4def40ee33 | ||
|
|
341fbe5486 | ||
|
|
898e207bcf | ||
|
|
619b8eb4d0 | ||
|
|
ebca982365 |
+11
-3
@@ -12,15 +12,23 @@ parameters:
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
release_latest:
|
||||
run_nightly_workflow:
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
release_version:
|
||||
default: "9999"
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
run_nightly_workflow:
|
||||
release_monorepo_packages_version:
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
release_tag:
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
release_dry_run:
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
|
||||
@@ -35,11 +35,11 @@ executors:
|
||||
xcode: *xcode_version
|
||||
resource_class: macos.x86.medium.gen2
|
||||
environment:
|
||||
- BUILD_FROM_SOURCE: true
|
||||
- RCT_BUILD_HERMES_FROM_SOURCE: true
|
||||
reactnativeios-lts:
|
||||
<<: *defaults
|
||||
macos:
|
||||
xcode: '14.3.1'
|
||||
resource_class: macos.x86.medium.gen2
|
||||
environment:
|
||||
- BUILD_FROM_SOURCE: true
|
||||
- RCT_BUILD_HERMES_FROM_SOURCE: true
|
||||
|
||||
@@ -599,7 +599,7 @@ jobs:
|
||||
environment:
|
||||
- HERMES_WS_DIR: *hermes_workspace_root
|
||||
- HERMES_VERSION_FILE: "packages/react-native/sdks/.hermesversion"
|
||||
- BUILD_FROM_SOURCE: true
|
||||
- RCT_BUILD_HERMES_FROM_SOURCE: true
|
||||
steps:
|
||||
- run:
|
||||
name: Install dependencies
|
||||
@@ -813,13 +813,19 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export RELEASE_VERSION=$(cat /tmp/react-native-version)
|
||||
if [[ "$SLICE" == "macosx" ]]; then
|
||||
echo "[HERMES] Building Hermes for MacOS"
|
||||
export MAC_DEPLOYMENT_TARGET=10.13
|
||||
BUILD_TYPE="<< parameters.flavor >>" ./utils/build-mac-framework.sh
|
||||
unset MAC_DEPLOYMENT_TARGET
|
||||
else
|
||||
echo "[HERMES] Building Hermes for iOS: $SLICE"
|
||||
export IOS_DEPLOYMENT_TARGET=13.4
|
||||
BUILD_TYPE="<< parameters.flavor >>" ./utils/build-ios-framework.sh "$SLICE"
|
||||
unset IOS_DEPLOYMENT_TARGET
|
||||
fi
|
||||
unset RELEASE_VERSION
|
||||
|
||||
echo "Moving from build_$SLICE to $FINAL_PATH"
|
||||
mv build_"$SLICE" "$FINAL_PATH"
|
||||
@@ -898,7 +904,11 @@ jobs:
|
||||
command: |
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
echo "[HERMES] Creating the universal framework"
|
||||
export IOS_DEPLOYMENT_TARGET=13.4
|
||||
export RELEASE_VERSION=$(cat /tmp/react-native-version)
|
||||
./utils/build-ios-framework.sh build_framework
|
||||
unset RELEASE_VERSION
|
||||
unset IOS_DEPLOYMENT_TARGET
|
||||
- run:
|
||||
name: Package the Hermes Apple frameworks
|
||||
command: |
|
||||
@@ -1076,14 +1086,20 @@ jobs:
|
||||
# -------------------------
|
||||
# JOBS: Releases
|
||||
# -------------------------
|
||||
prepare_package_for_release:
|
||||
|
||||
# Writes a new commit and tag(s), which will trigger the `publish_release`
|
||||
# and `publish_bumped_packages` workflows.
|
||||
prepare_release:
|
||||
parameters:
|
||||
version:
|
||||
type: string
|
||||
latest:
|
||||
type: boolean
|
||||
default: false
|
||||
dryrun:
|
||||
# TODO(T182538198): Required for 0.74.x, where workspace packages are out
|
||||
# of sync with react-native. This will be removed for 0.75+.
|
||||
monorepo_packages_version:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
dry_run:
|
||||
type: boolean
|
||||
default: false
|
||||
executor: reactnativeios
|
||||
@@ -1096,16 +1112,36 @@ jobs:
|
||||
- brew_install:
|
||||
package: cmake
|
||||
- run:
|
||||
name: "Set new react-native version and commit changes"
|
||||
name: Versioning workspace packages
|
||||
command: |
|
||||
VERSION=<< parameters.version >>
|
||||
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | head -1)
|
||||
echo "Using the version from the package.json: $VERSION"
|
||||
fi
|
||||
|
||||
node ./scripts/releases-ci/prepare-package-for-release.js -v "$VERSION" -l << parameters.latest >> --dry-run << parameters.dryrun >>
|
||||
node scripts/releases/set-version "<< parameters.monorepo_packages_version >>" --skip-react-native-version
|
||||
- run:
|
||||
name: Versioning react-native package
|
||||
command: |
|
||||
node scripts/releases/set-rn-version.js -v "<< parameters.version >>" --build-type "release"
|
||||
- run:
|
||||
name: Creating release commit
|
||||
command: |
|
||||
git commit -a -m "Release << parameters.version >>" -m "#publish-packages-to-npm&<< parameters.tag >>"
|
||||
git tag -a "v<< parameters.version >>" -m "v<< parameters.version >>"
|
||||
env GIT_PAGER=cat git show HEAD
|
||||
- when:
|
||||
condition:
|
||||
equal: ["latest", << parameters.tag >>]
|
||||
steps:
|
||||
- run:
|
||||
name: Updating "latest" tag
|
||||
command: |
|
||||
git tag -d "latest"
|
||||
git push origin :latest
|
||||
git tag -a "latest" -m "latest"
|
||||
- unless:
|
||||
condition: << parameters.dry_run >>
|
||||
steps:
|
||||
run:
|
||||
name: Pushing release commit
|
||||
command: |
|
||||
git push origin $CIRCLE_BRANCH --follow-tags
|
||||
|
||||
build_npm_package:
|
||||
parameters:
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
- equal: [ false, << pipeline.parameters.run_release_workflow >> ]
|
||||
- equal: [ false, << pipeline.parameters.run_nightly_workflow >> ]
|
||||
jobs:
|
||||
- prepare_package_for_release:
|
||||
name: prepare_package_for_release
|
||||
version: ''
|
||||
latest : false
|
||||
dryrun: true
|
||||
- prepare_release:
|
||||
name: "prepare_release (dry run test)"
|
||||
version: "0.0.0"
|
||||
monorepo_packages_version: "0.0.0"
|
||||
tag: test
|
||||
dry_run: true
|
||||
- prepare_hermes_workspace
|
||||
- build_android:
|
||||
release_type: "dry-run"
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
- equal: [ false, << pipeline.parameters.run_release_workflow >> ]
|
||||
- equal: [ false, << pipeline.parameters.run_nightly_workflow >> ]
|
||||
jobs:
|
||||
- prepare_package_for_release:
|
||||
name: prepare_package_for_release
|
||||
version: ''
|
||||
latest : false
|
||||
dryrun: true
|
||||
- prepare_release:
|
||||
name: "prepare_release (dry run test)"
|
||||
version: "0.0.0"
|
||||
monorepo_packages_version: "0.0.0"
|
||||
tag: test
|
||||
dry_run: true
|
||||
- prepare_hermes_workspace
|
||||
- build_android:
|
||||
release_type: "dry-run"
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
- equal: [ false, << pipeline.parameters.run_release_workflow >> ]
|
||||
- equal: [ false, << pipeline.parameters.run_nightly_workflow >> ]
|
||||
jobs:
|
||||
- prepare_package_for_release:
|
||||
name: prepare_package_for_release
|
||||
version: ''
|
||||
latest : false
|
||||
dryrun: true
|
||||
- prepare_release:
|
||||
name: "prepare_release (dry run test)"
|
||||
version: "0.0.0"
|
||||
monorepo_packages_version: "0.0.0"
|
||||
tag: test
|
||||
dry_run: true
|
||||
- prepare_hermes_workspace
|
||||
- build_android:
|
||||
release_type: "dry-run"
|
||||
|
||||
@@ -82,10 +82,10 @@ references:
|
||||
hermes_windows_cache_key: &hermes_windows_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-windows-{{ checksum "/Users/circleci/project/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
# Hermes iOS
|
||||
hermesc_apple_cache_key: &hermesc_apple_cache_key v3-hermesc-apple-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_apple_slices_cache_key: &hermes_apple_slices_cache_key v4-hermes-apple-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }}
|
||||
hermes_apple_slices_cache_key: &hermes_apple_slices_cache_key v7-hermes-apple-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }}
|
||||
hermes_tarball_debug_cache_key: &hermes_tarball_debug_cache_key v5-hermes-tarball-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }}
|
||||
hermes_tarball_release_cache_key: &hermes_tarball_release_cache_key v4-hermes-tarball-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }}
|
||||
hermes_macosx_bin_release_cache_key: &hermes_macosx_bin_release_cache_key v2-hermes-release-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_macosx_bin_release_cache_key: &hermes_macosx_bin_release_cache_key v4-hermes-release-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_macosx_bin_debug_cache_key: &hermes_macosx_bin_debug_cache_key v2-hermes-debug-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_dsym_debug_cache_key: &hermes_dsym_debug_cache_key v2-hermes-debug-dsym-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_dsym_release_cache_key: &hermes_dsym_release_cache_key v2-hermes-release-dsym-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
@@ -129,14 +129,22 @@ parameters:
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
release_latest:
|
||||
run_nightly_workflow:
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
release_version:
|
||||
default: "9999"
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
run_nightly_workflow:
|
||||
release_monorepo_packages_version:
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
release_tag:
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
release_dry_run:
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
@@ -15,15 +15,16 @@
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
# This workflow should only be triggered by release script
|
||||
package_release:
|
||||
# Release workflow, triggered by `yarn trigger-react-native-release`
|
||||
create_release:
|
||||
when: << pipeline.parameters.run_release_workflow >>
|
||||
jobs:
|
||||
# This job will push a tag that will trigger the publish_release workflow
|
||||
- prepare_package_for_release:
|
||||
name: prepare_package_for_release
|
||||
- prepare_release:
|
||||
name: prepare_release
|
||||
version: << pipeline.parameters.release_version >>
|
||||
latest : << pipeline.parameters.release_latest >>
|
||||
monorepo_packages_version: << pipeline.parameters.release_monorepo_packages_version >>
|
||||
tag: << pipeline.parameters.release_tag >>
|
||||
dry_run: << pipeline.parameters.release_dry_run >>
|
||||
|
||||
# This job will run only when a tag is published due to all the jobs being filtered.
|
||||
publish_release:
|
||||
|
||||
@@ -42,6 +42,7 @@ project.xcworkspace
|
||||
/packages/react-native/ReactAndroid/hermes-engine/.cxx/
|
||||
/packages/react-native/template/android/app/build/
|
||||
/packages/react-native/template/android/build/
|
||||
/packages/react-native-popup-menu-android/android/build/
|
||||
|
||||
# Buck
|
||||
.buckd
|
||||
|
||||
+3
-4
@@ -14,7 +14,6 @@
|
||||
"android": "cd packages/rn-tester && npm run android",
|
||||
"build-android": "./gradlew :packages:react-native:ReactAndroid:build",
|
||||
"build": "node ./scripts/build/build.js",
|
||||
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages",
|
||||
"clang-format": "clang-format -i --glob=*/**/*.{h,cpp,m,mm}",
|
||||
"clean": "node ./scripts/build/clean.js",
|
||||
"flow-check": "flow check",
|
||||
@@ -59,10 +58,10 @@
|
||||
"@definitelytyped/dtslint": "^0.0.127",
|
||||
"@jest/create-cache-key-function": "^29.6.3",
|
||||
"@pkgjs/parseargs": "^0.11.0",
|
||||
"@react-native/metro-babel-transformer": "0.74.0",
|
||||
"@react-native/metro-config": "0.74.0",
|
||||
"@react-native/metro-babel-transformer": "0.74.78",
|
||||
"@react-native/metro-config": "0.74.78",
|
||||
"@tsconfig/node18": "1.0.1",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react": "^18.2.6",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
"ansi-styles": "^4.2.1",
|
||||
"async": "^3.2.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/assets-registry",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Asset support code for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/babel-plugin-codegen",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Babel plugin to generate native module and view manager code for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -25,7 +25,7 @@
|
||||
"index.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@react-native/codegen": "0.74.0"
|
||||
"@react-native/codegen": "0.74.78"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/community-cli-plugin",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Core CLI commands for React Native",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@@ -22,10 +22,10 @@
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-server-api": "13.6.0",
|
||||
"@react-native-community/cli-tools": "13.6.0",
|
||||
"@react-native/dev-middleware": "0.74.0",
|
||||
"@react-native/metro-babel-transformer": "0.74.0",
|
||||
"@react-native-community/cli-server-api": "13.6.4",
|
||||
"@react-native-community/cli-tools": "13.6.4",
|
||||
"@react-native/dev-middleware": "0.74.78",
|
||||
"@react-native/metro-babel-transformer": "0.74.78",
|
||||
"chalk": "^4.0.0",
|
||||
"execa": "^5.1.1",
|
||||
"metro": "^0.80.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/debugger-frontend",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Debugger frontend for React Native based on Chrome DevTools",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/dev-middleware",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Dev server middleware for React Native",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@@ -23,7 +23,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@isaacs/ttlcache": "^1.4.1",
|
||||
"@react-native/debugger-frontend": "0.74.0",
|
||||
"@react-native/debugger-frontend": "0.74.78",
|
||||
"@rnx-kit/chromium-edge-launcher": "^1.0.0",
|
||||
"chrome-launcher": "^0.15.2",
|
||||
"connect": "^3.6.5",
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
import {fetchJson} from './FetchUtils';
|
||||
import {createDebuggerMock} from './InspectorDebuggerUtils';
|
||||
import {createDeviceMock} from './InspectorDeviceUtils';
|
||||
import {createAndConnectTarget} from './InspectorProtocolUtils';
|
||||
import {withAbortSignalForEachTest} from './ResourceUtils';
|
||||
import {baseUrlForServer, createServer} from './ServerUtils';
|
||||
import invariant from 'invariant';
|
||||
import until from 'wait-for-expect';
|
||||
|
||||
// WebSocket is unreliable when using fake timers.
|
||||
jest.useRealTimers();
|
||||
|
||||
jest.setTimeout(10000);
|
||||
|
||||
describe('inspector proxy device message middleware', () => {
|
||||
const autoCleanup = withAbortSignalForEachTest();
|
||||
const page = {
|
||||
id: 'page1',
|
||||
app: 'bar-app',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('middleware is created with device, debugger, and page information', async () => {
|
||||
const createCustomMessageHandler = jest.fn().mockImplementation(() => null);
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: createCustomMessageHandler,
|
||||
});
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
({device, debugger_} = await createAndConnectTarget(
|
||||
serverRefUrls(server),
|
||||
autoCleanup.signal,
|
||||
page,
|
||||
));
|
||||
|
||||
// Ensure the middleware was created with the device information
|
||||
await until(() =>
|
||||
expect(createCustomMessageHandler).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
page: expect.objectContaining({
|
||||
...page,
|
||||
capabilities: expect.any(Object),
|
||||
}),
|
||||
device: expect.objectContaining({
|
||||
appId: expect.any(String),
|
||||
id: expect.any(String),
|
||||
name: expect.any(String),
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
debugger: expect.objectContaining({
|
||||
userAgent: null,
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('middleware is created with device, debugger, and page information for synthetic reloadable page', async () => {
|
||||
const createCustomMessageHandler = jest.fn().mockImplementation(() => null);
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: createCustomMessageHandler,
|
||||
});
|
||||
const {serverBaseUrl, serverBaseWsUrl} = serverRefUrls(server);
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
device = await createDeviceMock(
|
||||
`${serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
// Mock the device to return a normal React (Native) page
|
||||
device.getPages.mockImplementation(() => [
|
||||
{
|
||||
...page,
|
||||
// NOTE: 'React' is a magic string used to detect React Native pages.
|
||||
title: 'React Native (mock)',
|
||||
},
|
||||
]);
|
||||
|
||||
// Retrieve the full page list from device
|
||||
let pageList;
|
||||
await until(async () => {
|
||||
pageList = (await fetchJson(
|
||||
`${serverBaseUrl}/json`,
|
||||
// $FlowIgnore[unclear-type]
|
||||
): any);
|
||||
expect(pageList.length).toBeGreaterThan(0);
|
||||
});
|
||||
invariant(pageList != null, '');
|
||||
|
||||
// Find the synthetic page
|
||||
const syntheticPage = pageList.find(
|
||||
({title}) =>
|
||||
// NOTE: Magic string used for the synthetic page that has a stable ID
|
||||
title === 'React Native Experimental (Improved Chrome Reloads)',
|
||||
);
|
||||
expect(syntheticPage).not.toBeUndefined();
|
||||
|
||||
// Connect the debugger to this synthetic page
|
||||
debugger_ = await createDebuggerMock(
|
||||
syntheticPage.webSocketDebuggerUrl,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
|
||||
// Ensure the middleware was created with the device information
|
||||
await until(() =>
|
||||
expect(createCustomMessageHandler).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
page: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: syntheticPage.title,
|
||||
vm: syntheticPage.vm,
|
||||
app: expect.any(String),
|
||||
capabilities: expect.any(Object),
|
||||
}),
|
||||
device: expect.objectContaining({
|
||||
appId: expect.any(String),
|
||||
id: expect.any(String),
|
||||
name: expect.any(String),
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
debugger: expect.objectContaining({
|
||||
userAgent: null,
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('send message functions are passing messages to sockets', async () => {
|
||||
const handleDebuggerMessage = jest.fn();
|
||||
const handleDeviceMessage = jest.fn();
|
||||
const createCustomMessageHandler = jest.fn().mockImplementation(() => ({
|
||||
handleDebuggerMessage,
|
||||
handleDeviceMessage,
|
||||
}));
|
||||
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: createCustomMessageHandler,
|
||||
});
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
({device, debugger_} = await createAndConnectTarget(
|
||||
serverRefUrls(server),
|
||||
autoCleanup.signal,
|
||||
page,
|
||||
));
|
||||
|
||||
// Ensure the middleware was created with the send message methods
|
||||
await until(() =>
|
||||
expect(createCustomMessageHandler).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
device: expect.objectContaining({
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
debugger: expect.objectContaining({
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// Send a message to the device
|
||||
createCustomMessageHandler.mock.calls[0][0].device.sendMessage({
|
||||
id: 1,
|
||||
});
|
||||
// Ensure the device received the message
|
||||
await until(() =>
|
||||
expect(device.wrappedEvent).toBeCalledWith({
|
||||
event: 'wrappedEvent',
|
||||
payload: {
|
||||
pageId: page.id,
|
||||
wrappedEvent: JSON.stringify({id: 1}),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Send a message to the debugger
|
||||
createCustomMessageHandler.mock.calls[0][0].debugger.sendMessage({
|
||||
id: 2,
|
||||
});
|
||||
// Ensure the debugger received the message
|
||||
await until(() =>
|
||||
expect(debugger_.handle).toBeCalledWith({
|
||||
id: 2,
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('device message is passed to message middleware', async () => {
|
||||
const handleDeviceMessage = jest.fn();
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: () => ({
|
||||
handleDeviceMessage,
|
||||
handleDebuggerMessage() {},
|
||||
}),
|
||||
});
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
({device, debugger_} = await createAndConnectTarget(
|
||||
serverRefUrls(server),
|
||||
autoCleanup.signal,
|
||||
page,
|
||||
));
|
||||
|
||||
// Send a message from the device, and ensure the middleware received it
|
||||
device.sendWrappedEvent(page.id, {id: 1337});
|
||||
|
||||
// Ensure the debugger received the message
|
||||
await until(() => expect(debugger_.handle).toBeCalledWith({id: 1337}));
|
||||
// Ensure the middleware received the message
|
||||
await until(() => expect(handleDeviceMessage).toBeCalled());
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('device message stops propagating when handled by middleware', async () => {
|
||||
const handleDeviceMessage = jest.fn();
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: () => ({
|
||||
handleDeviceMessage,
|
||||
handleDebuggerMessage() {},
|
||||
}),
|
||||
});
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
({device, debugger_} = await createAndConnectTarget(
|
||||
serverRefUrls(server),
|
||||
autoCleanup.signal,
|
||||
page,
|
||||
));
|
||||
|
||||
// Stop the first message from propagating by returning true (once) from middleware
|
||||
handleDeviceMessage.mockReturnValueOnce(true);
|
||||
|
||||
// Send the first message which should NOT be received by the debugger
|
||||
device.sendWrappedEvent(page.id, {id: -1});
|
||||
await until(() => expect(handleDeviceMessage).toBeCalled());
|
||||
|
||||
// Send the second message which should be received by the debugger
|
||||
device.sendWrappedEvent(page.id, {id: 1337});
|
||||
|
||||
// Ensure only the last message was received by the debugger
|
||||
await until(() => expect(debugger_.handle).toBeCalledWith({id: 1337}));
|
||||
// Ensure the first message was not received by the debugger
|
||||
expect(debugger_.handle).not.toBeCalledWith({id: -1});
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('debugger message is passed to message middleware', async () => {
|
||||
const handleDebuggerMessage = jest.fn();
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: () => ({
|
||||
handleDeviceMessage() {},
|
||||
handleDebuggerMessage,
|
||||
}),
|
||||
});
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
({device, debugger_} = await createAndConnectTarget(
|
||||
serverRefUrls(server),
|
||||
autoCleanup.signal,
|
||||
page,
|
||||
));
|
||||
|
||||
// Send a message from the debugger
|
||||
const message = {
|
||||
method: 'Runtime.enable',
|
||||
id: 1337,
|
||||
};
|
||||
debugger_.send(message);
|
||||
|
||||
// Ensure the device received the message
|
||||
await until(() => expect(device.wrappedEvent).toBeCalled());
|
||||
// Ensure the middleware received the message
|
||||
await until(() => expect(handleDebuggerMessage).toBeCalledWith(message));
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('debugger message stops propagating when handled by middleware', async () => {
|
||||
const handleDebuggerMessage = jest.fn();
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: () => ({
|
||||
handleDeviceMessage() {},
|
||||
handleDebuggerMessage,
|
||||
}),
|
||||
});
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
({device, debugger_} = await createAndConnectTarget(
|
||||
serverRefUrls(server),
|
||||
autoCleanup.signal,
|
||||
page,
|
||||
));
|
||||
|
||||
// Stop the first message from propagating by returning true (once) from middleware
|
||||
handleDebuggerMessage.mockReturnValueOnce(true);
|
||||
|
||||
// Send the first emssage which should not be received by the device
|
||||
debugger_.send({id: -1});
|
||||
// Send the second message which should be received by the device
|
||||
debugger_.send({id: 1337});
|
||||
|
||||
// Ensure only the last message was received by the device
|
||||
await until(() =>
|
||||
expect(device.wrappedEvent).toBeCalledWith({
|
||||
event: 'wrappedEvent',
|
||||
payload: {pageId: page.id, wrappedEvent: JSON.stringify({id: 1337})},
|
||||
}),
|
||||
);
|
||||
// Ensure the first message was not received by the device
|
||||
expect(device.wrappedEvent).not.toBeCalledWith({id: -1});
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function serverRefUrls(server: http$Server | https$Server) {
|
||||
return {
|
||||
serverBaseUrl: baseUrlForServer(server, 'http'),
|
||||
serverBaseWsUrl: baseUrlForServer(server, 'ws'),
|
||||
};
|
||||
}
|
||||
|
||||
async function closeServer(server: http$Server | https$Server): Promise<void> {
|
||||
return new Promise(resolve => server.close(() => resolve()));
|
||||
}
|
||||
@@ -14,11 +14,13 @@ import type {
|
||||
JsonVersionResponse,
|
||||
} from '../inspector-proxy/types';
|
||||
|
||||
import {fetchJson} from './FetchUtils';
|
||||
import {fetchJson, fetchLocal} from './FetchUtils';
|
||||
import {createDeviceMock} from './InspectorDeviceUtils';
|
||||
import {withAbortSignalForEachTest} from './ResourceUtils';
|
||||
import {withServerForEachTest} from './ServerUtils';
|
||||
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
// Must be greater than or equal to PAGES_POLLING_INTERVAL in `InspectorProxy.js`.
|
||||
const PAGES_POLLING_DELAY = 1000;
|
||||
|
||||
@@ -309,5 +311,67 @@ describe('inspector proxy HTTP API', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('handles Unicode data safely', async () => {
|
||||
const device = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
try {
|
||||
device.getPages.mockImplementation(() => [
|
||||
{
|
||||
app: 'bar-app 📱',
|
||||
id: 'page1 🛂',
|
||||
title: 'bar-title 📰',
|
||||
vm: 'bar-vm 🤖',
|
||||
},
|
||||
]);
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const json = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
expect(json).toEqual([
|
||||
expect.objectContaining({
|
||||
description: 'bar-app 📱',
|
||||
deviceName: 'foo',
|
||||
id: 'device1-page1 🛂',
|
||||
title: 'bar-title 📰',
|
||||
vm: 'bar-vm 🤖',
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
device.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('includes a valid Content-Length header', async () => {
|
||||
// NOTE: This test is needed because chrome://inspect's HTTP client is picky
|
||||
// and doesn't accept responses without a Content-Length header.
|
||||
const device = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
try {
|
||||
device.getPages.mockImplementation(() => [
|
||||
{
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
]);
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const response = await fetchLocal(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
expect(response.headers.get('Content-Length')).not.toBeNull();
|
||||
} finally {
|
||||
device.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {CreateCustomMessageHandlerFn} from './inspector-proxy/CustomMessageHandler';
|
||||
import type {BrowserLauncher} from './types/BrowserLauncher';
|
||||
import type {EventReporter} from './types/EventReporter';
|
||||
import type {Experiments, ExperimentsConfig} from './types/Experiments';
|
||||
@@ -61,11 +62,12 @@ type Options = $ReadOnly<{
|
||||
unstable_experiments?: ExperimentsConfig,
|
||||
|
||||
/**
|
||||
* An interface for using a modified inspector proxy implementation.
|
||||
* Create custom handler to add support for unsupported CDP events, or debuggers.
|
||||
* This handler is instantiated per logical device and debugger pair.
|
||||
*
|
||||
* This is an unstable API with no semver guarantees.
|
||||
*/
|
||||
unstable_InspectorProxy?: Class<InspectorProxy>,
|
||||
unstable_customInspectorMessageHandler?: CreateCustomMessageHandlerFn,
|
||||
}>;
|
||||
|
||||
type DevMiddlewareAPI = $ReadOnly<{
|
||||
@@ -80,16 +82,16 @@ export default function createDevMiddleware({
|
||||
unstable_browserLauncher = DefaultBrowserLauncher,
|
||||
unstable_eventReporter,
|
||||
unstable_experiments: experimentConfig = {},
|
||||
unstable_InspectorProxy,
|
||||
unstable_customInspectorMessageHandler,
|
||||
}: Options): DevMiddlewareAPI {
|
||||
const experiments = getExperiments(experimentConfig);
|
||||
|
||||
const InspectorProxyClass = unstable_InspectorProxy ?? InspectorProxy;
|
||||
const inspectorProxy = new InspectorProxyClass(
|
||||
const inspectorProxy = new InspectorProxy(
|
||||
projectRoot,
|
||||
serverBaseUrl,
|
||||
unstable_eventReporter,
|
||||
experiments,
|
||||
unstable_customInspectorMessageHandler,
|
||||
);
|
||||
|
||||
const middleware = connect()
|
||||
|
||||
@@ -13,6 +13,8 @@ export {default as createDevMiddleware} from './createDevMiddleware';
|
||||
|
||||
export type {BrowserLauncher, LaunchedBrowser} from './types/BrowserLauncher';
|
||||
export type {EventReporter, ReportableEvent} from './types/EventReporter';
|
||||
|
||||
export {default as unstable_InspectorProxy} from './inspector-proxy/InspectorProxy';
|
||||
export {default as unstable_Device} from './inspector-proxy/Device';
|
||||
export type {
|
||||
CustomMessageHandler,
|
||||
CustomMessageHandlerConnection,
|
||||
CreateCustomMessageHandlerFn,
|
||||
} from './inspector-proxy/CustomMessageHandler';
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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 {JSONSerializable, Page} from './types';
|
||||
|
||||
type ExposedDevice = $ReadOnly<{
|
||||
appId: string,
|
||||
id: string,
|
||||
name: string,
|
||||
sendMessage: (message: JSONSerializable) => void,
|
||||
}>;
|
||||
|
||||
type ExposedDebugger = $ReadOnly<{
|
||||
userAgent: string | null,
|
||||
sendMessage: (message: JSONSerializable) => void,
|
||||
}>;
|
||||
|
||||
export type CustomMessageHandlerConnection = $ReadOnly<{
|
||||
page: Page,
|
||||
device: ExposedDevice,
|
||||
debugger: ExposedDebugger,
|
||||
}>;
|
||||
|
||||
export type CreateCustomMessageHandlerFn = (
|
||||
connection: CustomMessageHandlerConnection,
|
||||
) => ?CustomMessageHandler;
|
||||
|
||||
/**
|
||||
* The device message middleware allows implementers to handle unsupported CDP messages.
|
||||
* It is instantiated per device and may contain state that is specific to that device.
|
||||
* The middleware can also mark messages from the device or debugger as handled, which stops propagating.
|
||||
*/
|
||||
export interface CustomMessageHandler {
|
||||
/**
|
||||
* Handle a CDP message coming from the device.
|
||||
* This is invoked before the message is sent to the debugger.
|
||||
* When returning true, the message is considered handled and will not be sent to the debugger.
|
||||
*/
|
||||
handleDeviceMessage(message: JSONSerializable): true | void;
|
||||
|
||||
/**
|
||||
* Handle a CDP message coming from the debugger.
|
||||
* This is invoked before the message is sent to the device.
|
||||
* When returning true, the message is considered handled and will not be sent to the device.
|
||||
*/
|
||||
handleDebuggerMessage(message: JSONSerializable): true | void;
|
||||
}
|
||||
@@ -16,6 +16,10 @@ import type {
|
||||
CDPResponse,
|
||||
CDPServerMessage,
|
||||
} from './cdp-types/messages';
|
||||
import type {
|
||||
CreateCustomMessageHandlerFn,
|
||||
CustomMessageHandler,
|
||||
} from './CustomMessageHandler';
|
||||
import type {
|
||||
MessageFromDevice,
|
||||
MessageToDevice,
|
||||
@@ -51,6 +55,11 @@ type DebuggerInfo = {
|
||||
userAgent: string | null,
|
||||
};
|
||||
|
||||
type DebuggerConnection = {
|
||||
...DebuggerInfo,
|
||||
customHandler: ?CustomMessageHandler,
|
||||
};
|
||||
|
||||
const REACT_NATIVE_RELOADABLE_PAGE_ID = '-1';
|
||||
|
||||
/**
|
||||
@@ -74,7 +83,7 @@ export default class Device {
|
||||
#pages: $ReadOnlyMap<string, Page>;
|
||||
|
||||
// Stores information about currently connected debugger (if any).
|
||||
#debuggerConnection: ?DebuggerInfo = null;
|
||||
#debuggerConnection: ?DebuggerConnection = null;
|
||||
|
||||
// Last known Page ID of the React Native page.
|
||||
// This is used by debugger connections that don't have PageID specified
|
||||
@@ -97,6 +106,9 @@ export default class Device {
|
||||
|
||||
#pagesPollingIntervalId: ReturnType<typeof setInterval>;
|
||||
|
||||
// The device message middleware factory function allowing implementers to handle unsupported CDP messages.
|
||||
#createCustomMessageHandler: ?CreateCustomMessageHandlerFn;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
@@ -104,6 +116,7 @@ export default class Device {
|
||||
socket: WS,
|
||||
projectRoot: string,
|
||||
eventReporter: ?EventReporter,
|
||||
createMessageMiddleware: ?CreateCustomMessageHandlerFn,
|
||||
) {
|
||||
this.#id = id;
|
||||
this.#name = name;
|
||||
@@ -118,6 +131,7 @@ export default class Device {
|
||||
appId: app,
|
||||
})
|
||||
: null;
|
||||
this.#createCustomMessageHandler = createMessageMiddleware;
|
||||
|
||||
// $FlowFixMe[incompatible-call]
|
||||
this.#deviceSocket.on('message', (message: string) => {
|
||||
@@ -162,14 +176,7 @@ export default class Device {
|
||||
|
||||
getPagesList(): $ReadOnlyArray<Page> {
|
||||
if (this.#lastConnectedLegacyReactNativePage) {
|
||||
const reactNativeReloadablePage = {
|
||||
id: REACT_NATIVE_RELOADABLE_PAGE_ID,
|
||||
title: 'React Native Experimental (Improved Chrome Reloads)',
|
||||
vm: "don't use",
|
||||
app: this.#app,
|
||||
capabilities: {},
|
||||
};
|
||||
return [...this.#pages.values(), reactNativeReloadablePage];
|
||||
return [...this.#pages.values(), this.#createSyntheticPage()];
|
||||
} else {
|
||||
return [...this.#pages.values()];
|
||||
}
|
||||
@@ -205,16 +212,64 @@ export default class Device {
|
||||
prependedFilePrefix: false,
|
||||
pageId,
|
||||
userAgent: metadata.userAgent,
|
||||
customHandler: null,
|
||||
};
|
||||
|
||||
// TODO(moti): Handle null case explicitly, e.g. refuse to connect to
|
||||
// unknown pages.
|
||||
const page: ?Page = this.#pages.get(pageId);
|
||||
const page: ?Page =
|
||||
pageId === REACT_NATIVE_RELOADABLE_PAGE_ID
|
||||
? this.#createSyntheticPage()
|
||||
: this.#pages.get(pageId);
|
||||
|
||||
this.#debuggerConnection = debuggerInfo;
|
||||
|
||||
debug(`Got new debugger connection for page ${pageId} of ${this.#name}`);
|
||||
|
||||
if (page && this.#debuggerConnection && this.#createCustomMessageHandler) {
|
||||
this.#debuggerConnection.customHandler = this.#createCustomMessageHandler(
|
||||
{
|
||||
page,
|
||||
debugger: {
|
||||
userAgent: debuggerInfo.userAgent,
|
||||
sendMessage: message => {
|
||||
try {
|
||||
const payload = JSON.stringify(message);
|
||||
debug('(Debugger) <- (Proxy) (Device): ' + payload);
|
||||
socket.send(payload);
|
||||
} catch {}
|
||||
},
|
||||
},
|
||||
device: {
|
||||
appId: this.#app,
|
||||
id: this.#id,
|
||||
name: this.#name,
|
||||
sendMessage: message => {
|
||||
try {
|
||||
const payload = JSON.stringify({
|
||||
event: 'wrappedEvent',
|
||||
payload: {
|
||||
pageId: this.#mapToDevicePageId(pageId),
|
||||
wrappedEvent: JSON.stringify(message),
|
||||
},
|
||||
});
|
||||
debug('(Debugger) -> (Proxy) (Device): ' + payload);
|
||||
this.#deviceSocket.send(payload);
|
||||
} catch {}
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (this.#debuggerConnection.customHandler) {
|
||||
debug('Created new custom message handler for debugger connection');
|
||||
} else {
|
||||
debug(
|
||||
'Skipping new custom message handler for debugger connection, factory function returned null',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.#sendMessageToDevice({
|
||||
event: 'connect',
|
||||
payload: {
|
||||
@@ -231,6 +286,15 @@ export default class Device {
|
||||
frontendUserAgent: metadata.userAgent,
|
||||
});
|
||||
let processedReq = debuggerRequest;
|
||||
|
||||
if (
|
||||
this.#debuggerConnection?.customHandler?.handleDebuggerMessage(
|
||||
debuggerRequest,
|
||||
) === true
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!page || !this.#pageHasCapability(page, 'nativeSourceCodeFetching')) {
|
||||
processedReq = this.#interceptClientMessageForSourceFetching(
|
||||
debuggerRequest,
|
||||
@@ -311,6 +375,19 @@ export default class Device {
|
||||
return page.capabilities[flag] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the synthetic "React Native Experimental (Improved Chrome Reloads)" page.
|
||||
*/
|
||||
#createSyntheticPage(): Page {
|
||||
return {
|
||||
id: REACT_NATIVE_RELOADABLE_PAGE_ID,
|
||||
title: 'React Native Experimental (Improved Chrome Reloads)',
|
||||
vm: "don't use",
|
||||
app: this.#app,
|
||||
capabilities: {},
|
||||
};
|
||||
}
|
||||
|
||||
// Handles messages received from device:
|
||||
// 1. For getPages responses updates local #pages list.
|
||||
// 2. All other messages are forwarded to debugger as wrappedEvent.
|
||||
@@ -411,12 +488,21 @@ export default class Device {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.#debuggerConnection != null) {
|
||||
const debuggerConnection = this.#debuggerConnection;
|
||||
if (debuggerConnection != null) {
|
||||
if (
|
||||
debuggerConnection.customHandler?.handleDeviceMessage(
|
||||
parsedPayload,
|
||||
) === true
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrapping just to make flow happy :)
|
||||
// $FlowFixMe[unused-promise]
|
||||
this.#processMessageFromDeviceLegacy(
|
||||
parsedPayload,
|
||||
this.#debuggerConnection,
|
||||
debuggerConnection,
|
||||
pageId,
|
||||
).then(() => {
|
||||
const messageToSend = JSON.stringify(parsedPayload);
|
||||
@@ -499,7 +585,7 @@ export default class Device {
|
||||
// Allows to make changes in incoming message from device.
|
||||
async #processMessageFromDeviceLegacy(
|
||||
payload: CDPServerMessage,
|
||||
debuggerInfo: DebuggerInfo,
|
||||
debuggerInfo: DebuggerConnection,
|
||||
pageId: ?string,
|
||||
) {
|
||||
// TODO(moti): Handle null case explicitly, or ideally associate a copy
|
||||
@@ -616,7 +702,7 @@ export default class Device {
|
||||
*/
|
||||
#interceptClientMessageForSourceFetching(
|
||||
req: CDPClientMessage,
|
||||
debuggerInfo: DebuggerInfo,
|
||||
debuggerInfo: DebuggerConnection,
|
||||
socket: WS,
|
||||
): CDPClientMessage | null {
|
||||
switch (req.method) {
|
||||
@@ -633,7 +719,7 @@ export default class Device {
|
||||
|
||||
#processDebuggerSetBreakpointByUrl(
|
||||
req: CDPRequest<'Debugger.setBreakpointByUrl'>,
|
||||
debuggerInfo: DebuggerInfo,
|
||||
debuggerInfo: DebuggerConnection,
|
||||
): CDPRequest<'Debugger.setBreakpointByUrl'> {
|
||||
// If we replaced Android emulator's address to localhost we need to change it back.
|
||||
if (debuggerInfo.originalSourceURLAddress != null) {
|
||||
@@ -758,10 +844,6 @@ export default class Device {
|
||||
// Fetch text, raising an exception if the text could not be fetched,
|
||||
// or is too large.
|
||||
async #fetchText(url: URL): Promise<string> {
|
||||
if (!['localhost', '127.0.0.1'].includes(url.hostname)) {
|
||||
throw new Error('remote fetches not permitted');
|
||||
}
|
||||
|
||||
// $FlowFixMe[incompatible-call] Suppress arvr node-fetch flow error
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
import type {EventReporter} from '../types/EventReporter';
|
||||
import type {Experiments} from '../types/Experiments';
|
||||
import type {CreateCustomMessageHandlerFn} from './CustomMessageHandler';
|
||||
import type {
|
||||
JsonPagesListResponse,
|
||||
JsonVersionResponse,
|
||||
@@ -58,17 +59,22 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
|
||||
#experiments: Experiments;
|
||||
|
||||
// custom message handler factory allowing implementers to handle unsupported CDP messages.
|
||||
#customMessageHandler: ?CreateCustomMessageHandlerFn;
|
||||
|
||||
constructor(
|
||||
projectRoot: string,
|
||||
serverBaseUrl: string,
|
||||
eventReporter: ?EventReporter,
|
||||
experiments: Experiments,
|
||||
customMessageHandler: ?CreateCustomMessageHandlerFn,
|
||||
) {
|
||||
this.#projectRoot = projectRoot;
|
||||
this.#serverBaseUrl = serverBaseUrl;
|
||||
this.#devices = new Map();
|
||||
this.#eventReporter = eventReporter;
|
||||
this.#experiments = experiments;
|
||||
this.#customMessageHandler = customMessageHandler;
|
||||
}
|
||||
|
||||
getPageDescriptions(): Array<PageDescription> {
|
||||
@@ -167,6 +173,7 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
response.writeHead(200, {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Length': Buffer.byteLength(data).toString(),
|
||||
Connection: 'close',
|
||||
});
|
||||
response.end(data);
|
||||
@@ -203,6 +210,7 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
socket,
|
||||
this.#projectRoot,
|
||||
this.#eventReporter,
|
||||
this.#customMessageHandler,
|
||||
);
|
||||
|
||||
if (oldDevice) {
|
||||
@@ -257,7 +265,7 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
}
|
||||
|
||||
device.handleDebuggerConnection(socket, pageId, {
|
||||
userAgent: req.headers['user-agent'] ?? null,
|
||||
userAgent: req.headers['user-agent'] ?? query.userAgent ?? null,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
@@ -30,6 +30,13 @@ export type TargetCapabilityFlags = $ReadOnly<{
|
||||
* In the proxy, this disables source fetching emulation and host rewrites.
|
||||
*/
|
||||
nativeSourceCodeFetching?: boolean,
|
||||
|
||||
/**
|
||||
* The target supports native network inspection.
|
||||
*
|
||||
* In the proxy, this disables intercepting and storing network requests.
|
||||
*/
|
||||
nativeNetworkInspection?: boolean,
|
||||
}>;
|
||||
|
||||
// Page information received from the device. New page is created for
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/eslint-config",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "ESLint config for React Native",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -22,7 +22,7 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.20.0",
|
||||
"@react-native/eslint-plugin": "0.74.0",
|
||||
"@react-native/eslint-plugin": "0.74.78",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/eslint-plugin",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "ESLint rules for @react-native/eslint-config",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/eslint-plugin-specs",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "ESLint rules to validate NativeModule and Component Specs",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -31,7 +31,7 @@
|
||||
"@babel/eslint-parser": "^7.20.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.20.0",
|
||||
"@babel/preset-flow": "^7.20.0",
|
||||
"@react-native/codegen": "0.74.0",
|
||||
"@react-native/codegen": "0.74.78",
|
||||
"make-dir": "^2.1.0",
|
||||
"pirates": "^4.0.1",
|
||||
"source-map-support": "0.5.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/hermes-inspector-msggen",
|
||||
"version": "0.72.0",
|
||||
"version": "0.74.78",
|
||||
"private": true,
|
||||
"description": "Hermes Inspector Message Generator for React Native",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/metro-config",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Metro configuration for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -26,8 +26,8 @@
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@react-native/js-polyfills": "0.74.0",
|
||||
"@react-native/metro-babel-transformer": "0.74.0",
|
||||
"@react-native/js-polyfills": "0.74.78",
|
||||
"@react-native/metro-babel-transformer": "0.74.78",
|
||||
"metro-config": "^0.80.3",
|
||||
"metro-runtime": "^0.80.3"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/normalize-colors",
|
||||
"version": "0.74.1",
|
||||
"version": "0.74.78",
|
||||
"description": "Color normalization for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/js-polyfills",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Polyfills for React Native.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/babel-preset",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Babel preset for React Native applications",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
@@ -54,7 +54,7 @@
|
||||
"@babel/plugin-transform-typescript": "^7.5.0",
|
||||
"@babel/plugin-transform-unicode-regex": "^7.0.0",
|
||||
"@babel/template": "^7.0.0",
|
||||
"@react-native/babel-plugin-codegen": "0.74.0",
|
||||
"@react-native/babel-plugin-codegen": "0.74.78",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"react-refresh": "^0.14.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/metro-babel-transformer",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Babel transformer for React Native applications.",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
@@ -16,7 +16,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@react-native/babel-preset": "0.74.0",
|
||||
"@react-native/babel-preset": "0.74.78",
|
||||
"hermes-parser": "0.19.1",
|
||||
"nullthrows": "^1.1.1"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-native/bots",
|
||||
"description": "React Native Bots",
|
||||
"version": "0.0.0",
|
||||
"version": "0.74.78",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/codegen-typescript-test",
|
||||
"version": "0.0.1",
|
||||
"version": "0.74.78",
|
||||
"private": true,
|
||||
"description": "TypeScript related unit test for @react-native/codegen",
|
||||
"license": "MIT",
|
||||
@@ -19,7 +19,7 @@
|
||||
"prepare": "yarn run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native/codegen": "0.74.0"
|
||||
"@react-native/codegen": "0.74.78"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/codegen",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Code generation tools for React Native",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
+1
-1
@@ -462,7 +462,7 @@ module.exports = {
|
||||
'com.facebook.react.bridge.ReactApplicationContext',
|
||||
'com.facebook.react.bridge.ReactContextBaseJavaModule',
|
||||
'com.facebook.react.bridge.ReactMethod',
|
||||
'com.facebook.react.internal.turbomodule.core.interfaces.TurboModule',
|
||||
'com.facebook.react.turbomodule.core.interfaces.TurboModule',
|
||||
'com.facebook.proguard.annotations.DoNotStrip',
|
||||
'javax.annotation.Nonnull',
|
||||
]);
|
||||
|
||||
+9
-9
@@ -20,7 +20,7 @@ import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
@@ -66,7 +66,7 @@ import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -136,7 +136,7 @@ import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
@@ -178,7 +178,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class AliasTurboModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
@@ -224,7 +224,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NativeCameraRollManagerSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
@@ -272,7 +272,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NativeExceptionsManagerSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
@@ -338,7 +338,7 @@ import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -462,7 +462,7 @@ import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
@@ -500,7 +500,7 @@ import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NativeSampleTurboModule2Spec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/gradle-plugin",
|
||||
"version": "0.74.0",
|
||||
"version": "0.74.78",
|
||||
"description": "Gradle Plugin for React Native",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
+20
@@ -54,6 +54,26 @@ abstract class PreparePrefabHeadersTask : DefaultTask() {
|
||||
it.include("boost/detail/workaround.hpp")
|
||||
it.include("boost/operators.hpp")
|
||||
it.include("boost/preprocessor/**/*.hpp")
|
||||
// Headers needed for exposing rrc_text and rrc_textinput
|
||||
it.include("boost/container_hash/**/*.hpp")
|
||||
it.include("boost/detail/**/*.hpp")
|
||||
it.include("boost/intrusive/**/*.hpp")
|
||||
it.include("boost/iterator/**/*.hpp")
|
||||
it.include("boost/move/**/*.hpp")
|
||||
it.include("boost/mpl/**/*.hpp")
|
||||
it.include("boost/mp11/**/*.hpp")
|
||||
it.include("boost/describe/**/*.hpp")
|
||||
it.include("boost/preprocessor/**/*.hpp")
|
||||
it.include("boost/type_traits/**/*.hpp")
|
||||
it.include("boost/utility/**/*.hpp")
|
||||
it.include("boost/detail/workaround.hpp")
|
||||
it.include("boost/assert.hpp")
|
||||
it.include("boost/static_assert.hpp")
|
||||
it.include("boost/cstdint.hpp")
|
||||
it.include("boost/operators.hpp")
|
||||
it.include("boost/config.hpp")
|
||||
it.include("boost/utility.hpp")
|
||||
it.include("boost/version.hpp")
|
||||
it.into(File(outputFolder.asFile, headerPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("com.facebook.react")
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = libs.versions.compileSdk.get().toInt()
|
||||
buildToolsVersion = libs.versions.buildTools.get()
|
||||
namespace = "com.facebook.react.popupmenu"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
targetSdk = libs.versions.targetSdk.get().toInt()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions { jvmTarget = "17" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Build React Native from source
|
||||
implementation(project(":packages:react-native:ReactAndroid"))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# We want to have more fine grained control on the Java version for
|
||||
# ReactAndroid, therefore we disable RGNP Java version alignment mechanism
|
||||
react.internal.disableJavaVersionAlignment=true
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateComponentDescriptorH.js
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShadowNodes.h"
|
||||
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
using AndroidPopupMenuComponentDescriptor = ConcreteComponentDescriptor<AndroidPopupMenuShadowNode>;
|
||||
|
||||
} // namespace facebook::react
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateEventEmitterCpp.js
|
||||
*/
|
||||
|
||||
#include "EventEmitters.h"
|
||||
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
void AndroidPopupMenuEventEmitter::onSelectionChange(OnSelectionChange $event) const {
|
||||
dispatchEvent("selectionChange", [$event=std::move($event)](jsi::Runtime &runtime) {
|
||||
auto $payload = jsi::Object(runtime);
|
||||
$payload.setProperty(runtime, "item", $event.item);
|
||||
return $payload;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateEventEmitterH.js
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <react/renderer/components/view/ViewEventEmitter.h>
|
||||
|
||||
|
||||
namespace facebook::react {
|
||||
class AndroidPopupMenuEventEmitter : public ViewEventEmitter {
|
||||
public:
|
||||
using ViewEventEmitter::ViewEventEmitter;
|
||||
|
||||
struct OnSelectionChange {
|
||||
int item;
|
||||
};
|
||||
void onSelectionChange(OnSelectionChange value) const;
|
||||
};
|
||||
} // namespace facebook::react
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsCpp.js
|
||||
*/
|
||||
|
||||
#include "Props.h"
|
||||
#include <react/renderer/core/PropsParserContext.h>
|
||||
#include <react/renderer/core/propsConversions.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
AndroidPopupMenuProps::AndroidPopupMenuProps(
|
||||
const PropsParserContext &context,
|
||||
const AndroidPopupMenuProps &sourceProps,
|
||||
const RawProps &rawProps): ViewProps(context, sourceProps, rawProps),
|
||||
|
||||
menuItems(convertRawProp(context, rawProps, "menuItems", sourceProps.menuItems, {}))
|
||||
{}
|
||||
|
||||
} // namespace facebook::react
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsH.js
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <react/renderer/components/view/ViewProps.h>
|
||||
#include <react/renderer/core/PropsParserContext.h>
|
||||
#include <vector>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
class AndroidPopupMenuProps final : public ViewProps {
|
||||
public:
|
||||
AndroidPopupMenuProps() = default;
|
||||
AndroidPopupMenuProps(const PropsParserContext& context, const AndroidPopupMenuProps &sourceProps, const RawProps &rawProps);
|
||||
|
||||
#pragma mark - Props
|
||||
|
||||
std::vector<std::string> menuItems{};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateShadowNodeCpp.js
|
||||
*/
|
||||
|
||||
#include "ShadowNodes.h"
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
extern const char AndroidPopupMenuComponentName[] = "AndroidPopupMenu";
|
||||
|
||||
} // namespace facebook::react
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateShadowNodeH.js
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EventEmitters.h"
|
||||
#include "Props.h"
|
||||
#include "States.h"
|
||||
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
JSI_EXPORT extern const char AndroidPopupMenuComponentName[];
|
||||
|
||||
/*
|
||||
* `ShadowNode` for <AndroidPopupMenu> component.
|
||||
*/
|
||||
using AndroidPopupMenuShadowNode = ConcreteViewShadowNode<
|
||||
AndroidPopupMenuComponentName,
|
||||
AndroidPopupMenuProps,
|
||||
AndroidPopupMenuEventEmitter,
|
||||
AndroidPopupMenuState>;
|
||||
|
||||
} // namespace facebook::react
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateStateCpp.js
|
||||
*/
|
||||
#include "States.h"
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
|
||||
|
||||
} // namespace facebook::react
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateStateH.js
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <folly/dynamic.h>
|
||||
#include <react/renderer/mapbuffer/MapBuffer.h>
|
||||
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
|
||||
#endif
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
class AndroidPopupMenuState {
|
||||
public:
|
||||
AndroidPopupMenuState() = default;
|
||||
|
||||
#ifdef ANDROID
|
||||
AndroidPopupMenuState(AndroidPopupMenuState const &previousState, folly::dynamic data){};
|
||||
folly::dynamic getDynamic() const {
|
||||
return {};
|
||||
};
|
||||
MapBuffer getMapBuffer() const {
|
||||
return MapBufferBuilder::EMPTY();
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.popupmenu
|
||||
|
||||
import com.facebook.react.BaseReactPackage
|
||||
import com.facebook.react.ViewManagerOnDemandReactPackage
|
||||
import com.facebook.react.bridge.ModuleSpec
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.module.annotations.ReactModuleList
|
||||
import com.facebook.react.module.model.ReactModuleInfoProvider
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
|
||||
@ReactModuleList(nativeModules = arrayOf())
|
||||
class PopupMenuPackage() : BaseReactPackage(), ViewManagerOnDemandReactPackage {
|
||||
private var viewManagersMap: Map<String, ModuleSpec>? = null
|
||||
|
||||
override fun getModule(name: String, context: ReactApplicationContext): NativeModule? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getViewManagersMap(): Map<String, ModuleSpec> {
|
||||
val viewManagers =
|
||||
viewManagersMap
|
||||
?: mapOf(
|
||||
ReactPopupMenuManager.REACT_CLASS to
|
||||
ModuleSpec.viewManagerSpec({ ReactPopupMenuManager() }))
|
||||
viewManagersMap = viewManagers
|
||||
return viewManagers
|
||||
}
|
||||
|
||||
protected override fun getViewManagers(context: ReactApplicationContext): List<ModuleSpec> {
|
||||
return ArrayList(getViewManagersMap().values)
|
||||
}
|
||||
|
||||
override fun getViewManagerNames(context: ReactApplicationContext): Collection<String> {
|
||||
return getViewManagersMap().keys
|
||||
}
|
||||
|
||||
override fun createViewManager(
|
||||
reactContext: ReactApplicationContext,
|
||||
viewManagerName: String
|
||||
): ViewManager<*, *>? {
|
||||
val spec: ModuleSpec? = getViewManagersMap().get(viewManagerName)
|
||||
return if (spec != null) (spec.getProvider().get() as ViewManager<*, *>) else null
|
||||
}
|
||||
|
||||
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
||||
return ReactModuleInfoProvider { emptyMap() }
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -5,9 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION") // We want to use RCTEventEmitter for interop purposes
|
||||
|
||||
package com.facebook.react.views.popupmenu
|
||||
package com.facebook.react.popupmenu
|
||||
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.popupmenu
|
||||
package com.facebook.react.popupmenu
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.popupmenu
|
||||
package com.facebook.react.popupmenu
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaDelegate.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
|
||||
public class AndroidPopupMenuManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & AndroidPopupMenuManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public AndroidPopupMenuManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "menuItems":
|
||||
mViewManager.setMenuItems(view, (ReadableArray) value);
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCommand(T view, String commandName, ReadableArray args) {
|
||||
switch (commandName) {
|
||||
case "show":
|
||||
mViewManager.show(view);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaInterface.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
||||
public interface AndroidPopupMenuManagerInterface<T extends View> {
|
||||
void setMenuItems(T view, @Nullable ReadableArray value);
|
||||
void show(T view);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export type {default} from './js/PopupMenuAndroid';
|
||||
export type {PopupMenuAndroidInstance} from './js/PopupMenuAndroid';
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export {default} from './js/PopupMenuAndroid';
|
||||
export type {PopupMenuAndroidInstance} from './js/PopupMenuAndroid';
|
||||
+3
-3
@@ -8,13 +8,13 @@
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes';
|
||||
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
|
||||
import type {RefObject} from 'react';
|
||||
import type {HostComponent} from 'react-native';
|
||||
import type {SyntheticEvent} from 'react-native/Libraries/Types/CoreEventTypes';
|
||||
|
||||
import PopupMenuAndroidNativeComponent, {
|
||||
Commands,
|
||||
} from './PopupMenuAndroidNativeComponent';
|
||||
} from './PopupMenuAndroidNativeComponent.android';
|
||||
import nullthrows from 'nullthrows';
|
||||
import * as React from 'react';
|
||||
import {useCallback, useImperativeHandle, useRef} from 'react';
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import type * as React from 'react';
|
||||
import {HostComponent} from '../../../types/public/ReactNativeTypes';
|
||||
import {HostComponent} from 'react-native';
|
||||
|
||||
type PopupMenuAndroidInstance = {
|
||||
show: () => void;
|
||||
+23
-2
@@ -12,8 +12,29 @@ import type {RefObject} from 'react';
|
||||
import type {Node} from 'react';
|
||||
|
||||
import * as React from 'react';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
|
||||
const UnimplementedView = require('../UnimplementedViews/UnimplementedView');
|
||||
/**
|
||||
* Common implementation for a simple stubbed view. Simply applies the view's styles to the inner
|
||||
* View component and renders its children.
|
||||
*/
|
||||
class UnimplementedView extends React.Component<{children: Node}> {
|
||||
render(): React.Node {
|
||||
return (
|
||||
<View style={[styles.unimplementedView]}>{this.props.children}</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
unimplementedView: __DEV__
|
||||
? {
|
||||
alignSelf: 'flex-start',
|
||||
borderColor: 'red',
|
||||
borderWidth: 1,
|
||||
}
|
||||
: {},
|
||||
});
|
||||
|
||||
export type PopupMenuAndroidInstance = {
|
||||
+show: () => void,
|
||||
@@ -27,7 +48,7 @@ type Props = {
|
||||
};
|
||||
|
||||
function PopupMenuAndroid(props: Props): Node {
|
||||
return <UnimplementedView />;
|
||||
return <UnimplementedView>{props.children}</UnimplementedView>;
|
||||
}
|
||||
|
||||
export default PopupMenuAndroid;
|
||||
+5
-5
@@ -8,16 +8,16 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
|
||||
import type {HostComponent} from '../../../../Libraries/Renderer/shims/ReactNativeTypes';
|
||||
import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
|
||||
import type {
|
||||
DirectEventHandler,
|
||||
Int32,
|
||||
} from '../../../../Libraries/Types/CodegenTypes';
|
||||
} from 'react-native/Libraries/Types/CodegenTypes';
|
||||
|
||||
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
|
||||
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
|
||||
import * as React from 'react';
|
||||
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
||||
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
||||
|
||||
type PopupMenuSelectionEvent = $ReadOnly<{
|
||||
item: Int32,
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@react-native/popup-menu-android",
|
||||
"version": "0.74.78",
|
||||
"description": "PopupMenu for the Android platform",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"js",
|
||||
"android",
|
||||
"!android/build",
|
||||
"!**/__tests__",
|
||||
"!**/__fixtures__",
|
||||
"!**/__mocks__"
|
||||
],
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"android"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@react-native/codegen": "0.74.78"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.2.6",
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"nullthrows": "^1.1.1"
|
||||
},
|
||||
"codegenConfig": {
|
||||
"name": "ReactPopupMenuAndroidSpecs",
|
||||
"type": "components",
|
||||
"jsSrcsDir": "js",
|
||||
"outputDir": {
|
||||
"android": "android"
|
||||
},
|
||||
"includesGeneratedCode": true,
|
||||
"android": {
|
||||
"javaPackageName": "com.facebook.react.viewmanagers"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"name": "@react-native/test-renderer",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"description": "A Test rendering library for React Native",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/plugin-transform-react-jsx": "^7.0.0",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/preset-flow": "^7.20.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"main": "src/index.js",
|
||||
"peerDependencies": { "jest": "^29.7.0" }
|
||||
"name": "@react-native/test-renderer",
|
||||
"private": true,
|
||||
"version": "0.74.78",
|
||||
"description": "A Test rendering library for React Native",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/plugin-transform-react-jsx": "^7.0.0",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/preset-flow": "^7.20.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"main": "src/index.js",
|
||||
"peerDependencies": {
|
||||
"jest": "^29.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RCTRootViewFactory.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@protocol RCTBridgeDelegate;
|
||||
@@ -57,9 +58,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The window object, used to render the UViewControllers
|
||||
@property (nonatomic, strong, nonnull) UIWindow *window;
|
||||
@property (nonatomic, strong, nullable) RCTBridge *bridge;
|
||||
@property (nonatomic, nullable) RCTBridge *bridge;
|
||||
@property (nonatomic, strong, nullable) NSString *moduleName;
|
||||
@property (nonatomic, strong, nullable) NSDictionary *initialProps;
|
||||
@property (nonatomic, strong, nonnull) RCTRootViewFactory *rootViewFactory;
|
||||
|
||||
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
/**
|
||||
* It creates a `RCTBridge` using a delegate and some launch options.
|
||||
@@ -126,13 +130,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
|
||||
|
||||
/// This method controls whether the App will use RuntimeScheduler. Only applicable in the legacy architecture.
|
||||
///
|
||||
/// @return: `YES` to use RuntimeScheduler, `NO` to use JavaScript scheduler. The default value is `YES`.
|
||||
- (BOOL)runtimeSchedulerEnabled;
|
||||
|
||||
@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
/// This method returns a map of Component Descriptors and Components classes that needs to be registered in the
|
||||
/// new renderer. The Component Descriptor is a string which represent the name used in JS to refer to the native
|
||||
/// component. The default implementation returns an empty dictionary. Subclasses can override this method to register
|
||||
|
||||
@@ -39,84 +39,28 @@
|
||||
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
|
||||
#import <react/runtime/JSRuntimeFactory.h>
|
||||
|
||||
@interface RCTAppDelegate () <
|
||||
RCTTurboModuleManagerDelegate,
|
||||
RCTComponentViewFactoryComponentProvider,
|
||||
RCTContextContainerHandling> {
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider, RCTTurboModuleManagerDelegate>
|
||||
@end
|
||||
|
||||
static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled)
|
||||
{
|
||||
NSMutableDictionary *mutableProps = [initialProps mutableCopy] ?: [NSMutableDictionary new];
|
||||
return mutableProps;
|
||||
}
|
||||
|
||||
@interface RCTAppDelegate () <RCTCxxBridgeDelegate> {
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation RCTAppDelegate {
|
||||
RCTHost *_reactHost;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@implementation RCTAppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTSetNewArchEnabled([self newArchEnabled]);
|
||||
BOOL enableTM = self.turboModuleEnabled;
|
||||
BOOL fabricEnabled = self.fabricEnabled;
|
||||
BOOL enableBridgeless = self.bridgelessEnabled;
|
||||
RCTAppSetupPrepareApp(application, self.turboModuleEnabled);
|
||||
|
||||
NSDictionary *initProps = updateInitialProps([self prepareInitialProps], fabricEnabled);
|
||||
self.rootViewFactory = [self createRCTRootViewFactory];
|
||||
|
||||
RCTAppSetupPrepareApp(application, enableTM);
|
||||
UIView *rootView = [self.rootViewFactory viewWithModuleName:self.moduleName
|
||||
initialProperties:self.initialProps
|
||||
launchOptions:launchOptions];
|
||||
|
||||
UIView *rootView;
|
||||
if (enableBridgeless) {
|
||||
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
|
||||
RCTSetUseNativeViewConfigsInBridgelessMode(fabricEnabled);
|
||||
|
||||
// Enable TurboModule interop by default in Bridgeless mode
|
||||
RCTEnableTurboModuleInterop(YES);
|
||||
RCTEnableTurboModuleInteropBridgeProxy(YES);
|
||||
|
||||
[self createReactHost];
|
||||
if (self.newArchEnabled || self.fabricEnabled) {
|
||||
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
|
||||
RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:self.moduleName initialProperties:initProps];
|
||||
|
||||
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
|
||||
initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
|
||||
|
||||
rootView = (RCTRootView *)surfaceHostingProxyRootView;
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
} else {
|
||||
if (!self.bridge) {
|
||||
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
|
||||
}
|
||||
if ([self newArchEnabled]) {
|
||||
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge
|
||||
contextContainer:_contextContainer];
|
||||
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;
|
||||
|
||||
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
|
||||
}
|
||||
rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps];
|
||||
}
|
||||
[self _logWarnIfCreateRootViewWithBridgeIsOverridden];
|
||||
[self customizeRootView:(RCTRootView *)rootView];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [self createRootViewController];
|
||||
[self setRootView:rootView toRootViewController:rootViewController];
|
||||
@@ -139,26 +83,15 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)prepareInitialProps
|
||||
{
|
||||
return self.initialProps;
|
||||
}
|
||||
|
||||
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (void)customizeRootView:(RCTRootView *)rootView
|
||||
{
|
||||
// Override point for customization after application launch.
|
||||
}
|
||||
|
||||
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
initProps:(NSDictionary *)initProps
|
||||
{
|
||||
[self _logWarnIfCreateRootViewWithBridgeIsOverridden];
|
||||
BOOL enableFabric = self.fabricEnabled;
|
||||
UIView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric);
|
||||
|
||||
@@ -192,9 +125,9 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
rootViewController.view = rootView;
|
||||
}
|
||||
|
||||
- (BOOL)runtimeSchedulerEnabled
|
||||
- (void)customizeRootView:(RCTRootView *)rootView
|
||||
{
|
||||
return YES;
|
||||
// Override point for customization after application launch.
|
||||
}
|
||||
|
||||
#pragma mark - UISceneDelegate
|
||||
@@ -207,25 +140,6 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
|
||||
}
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
if ([self newArchEnabled]) {
|
||||
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
|
||||
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
|
||||
RCTTurboModuleManager *turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
|
||||
delegate:self
|
||||
jsInvoker:callInvoker];
|
||||
_contextContainer->erase("RuntimeScheduler");
|
||||
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
|
||||
} else {
|
||||
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - New Arch Enabled settings
|
||||
|
||||
- (BOOL)newArchEnabled
|
||||
@@ -252,11 +166,33 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
return [self newArchEnabled];
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewFactoryComponentProvider
|
||||
|
||||
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return @{};
|
||||
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
|
||||
format:@"Subclasses must implement a valid getBundleURL method"];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#pragma mark - Bridge and Bridge Adapter properties
|
||||
|
||||
- (RCTBridge *)bridge
|
||||
{
|
||||
return self.rootViewFactory.bridge;
|
||||
}
|
||||
|
||||
- (RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
|
||||
{
|
||||
return self.rootViewFactory.bridgeAdapter;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
self.rootViewFactory.bridge = bridge;
|
||||
}
|
||||
|
||||
- (void)setBridgeAdapter:(RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
|
||||
{
|
||||
self.rootViewFactory.bridgeAdapter = bridgeAdapter;
|
||||
}
|
||||
|
||||
#pragma mark - RCTTurboModuleManagerDelegate
|
||||
@@ -288,43 +224,33 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
||||
}
|
||||
|
||||
#pragma mark - New Arch Utilities
|
||||
#pragma mark - RCTComponentViewFactoryComponentProvider
|
||||
|
||||
- (void)createReactHost
|
||||
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
|
||||
{
|
||||
return @{};
|
||||
}
|
||||
|
||||
- (RCTRootViewFactory *)createRCTRootViewFactory
|
||||
{
|
||||
RCTRootViewFactoryConfiguration *configuration =
|
||||
[[RCTRootViewFactoryConfiguration alloc] initWithBundleURL:self.bundleURL
|
||||
newArchEnabled:self.fabricEnabled
|
||||
turboModuleEnabled:self.turboModuleEnabled
|
||||
bridgelessEnabled:self.bridgelessEnabled];
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
_reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL]
|
||||
hostDelegate:nil
|
||||
turboModuleManagerDelegate:self
|
||||
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
|
||||
return [weakSelf createJSRuntimeFactory];
|
||||
}];
|
||||
[_reactHost setBundleURLProvider:^NSURL *() {
|
||||
return [weakSelf bundleURL];
|
||||
}];
|
||||
[_reactHost setContextContainerHandler:self];
|
||||
[_reactHost start];
|
||||
}
|
||||
configuration.createRootViewWithBridge = ^UIView *(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps)
|
||||
{
|
||||
return [weakSelf createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
|
||||
};
|
||||
|
||||
- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
|
||||
{
|
||||
#if USE_HERMES
|
||||
return std::make_shared<facebook::react::RCTHermesInstance>(_reactNativeConfig, nullptr);
|
||||
#else
|
||||
return std::make_shared<facebook::react::RCTJscInstance>();
|
||||
#endif
|
||||
}
|
||||
configuration.createBridgeWithDelegate = ^RCTBridge *(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions)
|
||||
{
|
||||
return [weakSelf createBridgeWithDelegate:delegate launchOptions:launchOptions];
|
||||
};
|
||||
|
||||
- (void)didCreateContextContainer:(std::shared_ptr<facebook::react::ContextContainer>)contextContainer
|
||||
{
|
||||
contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
|
||||
format:@"Subclasses must implement a valid getBundleURL method"];
|
||||
return nullptr;
|
||||
return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
@protocol RCTCxxBridgeDelegate;
|
||||
@protocol RCTComponentViewFactoryComponentProvider;
|
||||
@protocol RCTTurboModuleManagerDelegate;
|
||||
@class RCTBridge;
|
||||
@class RCTRootView;
|
||||
@class RCTSurfacePresenterBridgeAdapter;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Blocks' definitions
|
||||
typedef UIView *_Nonnull (
|
||||
^RCTCreateRootViewWithBridgeBlock)(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps);
|
||||
typedef RCTBridge *_Nonnull (
|
||||
^RCTCreateBridgeWithDelegateBlock)(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions);
|
||||
|
||||
#pragma mark - RCTRootViewFactory Configuration
|
||||
@interface RCTRootViewFactoryConfiguration : NSObject
|
||||
|
||||
/// This property controls whether the App will use the Fabric renderer of the New Architecture or not.
|
||||
@property (nonatomic, assign, readonly) BOOL fabricEnabled;
|
||||
|
||||
/// This property controls whether React Native's new initialization layer is enabled.
|
||||
@property (nonatomic, assign, readonly) BOOL bridgelessEnabled;
|
||||
|
||||
/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off
|
||||
@property (nonatomic, assign, readonly) BOOL turboModuleEnabled;
|
||||
|
||||
/// Return the bundle URL for the main bundle.
|
||||
@property (nonatomic) NSURL *bundleURL;
|
||||
|
||||
/**
|
||||
* Use this method to initialize a new instance of `RCTRootViewFactoryConfiguration` by passing a `bundleURL`
|
||||
*
|
||||
* Which is the location of the JavaScript source file. When running from the packager
|
||||
* this should be an absolute URL, e.g. `http://localhost:8081/index.ios.bundle`.
|
||||
* When running from a locally bundled JS file, this should be a `file://` url
|
||||
* pointing to a path inside the app resources, e.g. `file://.../main.jsbundle`.
|
||||
*
|
||||
*/
|
||||
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
||||
newArchEnabled:(BOOL)newArchEnabled
|
||||
turboModuleEnabled:(BOOL)turboModuleEnabled
|
||||
bridgelessEnabled:(BOOL)bridgelessEnabled;
|
||||
|
||||
/**
|
||||
* Block that allows to override logic of creating root view instance.
|
||||
* It creates a `UIView` starting from a bridge, a module name and a set of initial properties.
|
||||
* By default, it is invoked using the bridge created by `RCTCreateBridgeWithDelegateBlock` (or the default
|
||||
* implementation) and the `moduleName` variable comes from `viewWithModuleName:initialProperties:launchOptions` of
|
||||
* `RCTRootViewFactory`.
|
||||
*
|
||||
* @parameter: bridge - an instance of the `RCTBridge` object.
|
||||
* @parameter: moduleName - the name of the app, used by Metro to resolve the module.
|
||||
* @parameter: initProps - a set of initial properties.
|
||||
*
|
||||
* @returns: a UIView properly configured with a bridge for React Native.
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTCreateRootViewWithBridgeBlock createRootViewWithBridge;
|
||||
|
||||
/**
|
||||
* Block that allows to override default behavior of creating bridge.
|
||||
* It should return `RCTBridge` using a delegate and some launch options.
|
||||
*
|
||||
* By default, it is invoked passing `self` as a delegate.
|
||||
*
|
||||
* @parameter: delegate - an object that implements the `RCTBridgeDelegate` protocol.
|
||||
* @parameter: launchOptions - a dictionary with a set of options.
|
||||
*
|
||||
* @returns: a newly created instance of RCTBridge.
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTCreateBridgeWithDelegateBlock createBridgeWithDelegate;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - RCTRootViewFactory
|
||||
/**
|
||||
* The RCTRootViewFactory is an utility class that encapsulates the logic of creating a new RCTRootView based on the
|
||||
* current state of the environment. It allows you to initialize your app root view for old architecture, new
|
||||
* architecture and bridgless mode.
|
||||
*
|
||||
* This class is used to initalize rootView in RCTAppDelegate, but you can also use it separately.
|
||||
*
|
||||
* Create a new instance of this class (make sure to retain it) and call the
|
||||
* `viewWithModuleName:initialProperties:launchOptions` method to create new RCTRootView.
|
||||
*/
|
||||
@interface RCTRootViewFactory : NSObject
|
||||
|
||||
@property (nonatomic, strong, nullable) RCTBridge *bridge;
|
||||
@property (nonatomic, strong, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration
|
||||
andTurboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate;
|
||||
|
||||
/**
|
||||
* This method can be used to create new RCTRootViews on demand.
|
||||
*
|
||||
* @parameter: moduleName - the name of the app, used by Metro to resolve the module.
|
||||
* @parameter: initialProperties - a set of initial properties.
|
||||
* @parameter: launchOptions - a dictionary with a set of options.
|
||||
*/
|
||||
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName
|
||||
initialProperties:(NSDictionary *__nullable)initialProperties
|
||||
launchOptions:(NSDictionary *__nullable)launchOptions;
|
||||
|
||||
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName
|
||||
initialProperties:(NSDictionary *__nullable)initialProperties;
|
||||
|
||||
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import "RCTRootViewFactory.h"
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import "RCTAppDelegate.h"
|
||||
#import "RCTAppSetupUtils.h"
|
||||
|
||||
#if RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
#import <RCTTurboModulePlugin/RCTTurboModulePlugin.h>
|
||||
#else
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#endif
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTComponentViewFactory.h>
|
||||
#import <React/RCTComponentViewProtocol.h>
|
||||
#import <React/RCTFabricSurface.h>
|
||||
#import <React/RCTSurfaceHostingProxyRootView.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <ReactCommon/RCTContextContainerHandling.h>
|
||||
#if USE_HERMES
|
||||
#import <ReactCommon/RCTHermesInstance.h>
|
||||
#else
|
||||
#import <ReactCommon/RCTJscInstance.h>
|
||||
#endif
|
||||
#import <ReactCommon/RCTHost+Internal.h>
|
||||
#import <ReactCommon/RCTHost.h>
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
|
||||
#import <react/runtime/JSRuntimeFactory.h>
|
||||
|
||||
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||
|
||||
static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled)
|
||||
{
|
||||
NSMutableDictionary *mutableProps = initialProps != NULL ? [initialProps mutableCopy] : [NSMutableDictionary new];
|
||||
// Hardcoding the Concurrent Root as it it not recommended to
|
||||
// have the concurrentRoot turned off when Fabric is enabled.
|
||||
mutableProps[kRNConcurrentRoot] = @(isFabricEnabled);
|
||||
return mutableProps;
|
||||
}
|
||||
|
||||
@implementation RCTRootViewFactoryConfiguration
|
||||
|
||||
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
||||
newArchEnabled:(BOOL)newArchEnabled
|
||||
turboModuleEnabled:(BOOL)turboModuleEnabled
|
||||
bridgelessEnabled:(BOOL)bridgelessEnabled
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bundleURL = bundleURL;
|
||||
_fabricEnabled = newArchEnabled;
|
||||
_turboModuleEnabled = turboModuleEnabled;
|
||||
_bridgelessEnabled = bridgelessEnabled;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTRootViewFactory () <RCTContextContainerHandling> {
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface RCTRootViewFactory () <RCTCxxBridgeDelegate> {
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation RCTRootViewFactory {
|
||||
RCTHost *_reactHost;
|
||||
RCTRootViewFactoryConfiguration *_configuration;
|
||||
__weak id<RCTTurboModuleManagerDelegate> _turboModuleManagerDelegate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration
|
||||
andTurboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_configuration = configuration;
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
_turboModuleManagerDelegate = turboModuleManagerDelegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties
|
||||
{
|
||||
return [self viewWithModuleName:moduleName initialProperties:initialProperties launchOptions:nil];
|
||||
}
|
||||
|
||||
- (UIView *)viewWithModuleName:(NSString *)moduleName
|
||||
{
|
||||
return [self viewWithModuleName:moduleName initialProperties:nil launchOptions:nil];
|
||||
}
|
||||
|
||||
- (UIView *)viewWithModuleName:(NSString *)moduleName
|
||||
initialProperties:(NSDictionary *)initialProperties
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSDictionary *initProps = updateInitialProps(initialProperties, self->_configuration.fabricEnabled);
|
||||
|
||||
if (self->_configuration.bridgelessEnabled) {
|
||||
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
|
||||
RCTSetUseNativeViewConfigsInBridgelessMode(self->_configuration.fabricEnabled);
|
||||
|
||||
// Enable TurboModule interop by default in Bridgeless mode
|
||||
RCTEnableTurboModuleInterop(YES);
|
||||
RCTEnableTurboModuleInteropBridgeProxy(YES);
|
||||
|
||||
[self createReactHostIfNeeded];
|
||||
|
||||
RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps];
|
||||
|
||||
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
|
||||
initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
|
||||
|
||||
return surfaceHostingProxyRootView;
|
||||
}
|
||||
|
||||
[self createBridgeIfNeeded:launchOptions];
|
||||
[self createBridgeAdapterIfNeeded];
|
||||
|
||||
if (self->_configuration.createRootViewWithBridge != nil) {
|
||||
return self->_configuration.createRootViewWithBridge(self.bridge, moduleName, initProps);
|
||||
}
|
||||
|
||||
return [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps];
|
||||
}
|
||||
|
||||
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
initProps:(NSDictionary *)initProps
|
||||
{
|
||||
BOOL enableFabric = self->_configuration.fabricEnabled;
|
||||
UIView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric);
|
||||
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
if (RCTIsNewArchEnabled()) {
|
||||
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
|
||||
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
|
||||
RCTTurboModuleManager *turboModuleManager =
|
||||
[[RCTTurboModuleManager alloc] initWithBridge:bridge
|
||||
delegate:_turboModuleManagerDelegate
|
||||
jsInvoker:callInvoker];
|
||||
_contextContainer->erase("RuntimeScheduler");
|
||||
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
|
||||
} else {
|
||||
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createBridgeIfNeeded:(NSDictionary *)launchOptions
|
||||
{
|
||||
if (self.bridge != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->_configuration.createBridgeWithDelegate != nil) {
|
||||
self.bridge = self->_configuration.createBridgeWithDelegate(self, launchOptions);
|
||||
} else {
|
||||
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createBridgeAdapterIfNeeded
|
||||
{
|
||||
if (!self->_configuration.fabricEnabled || self.bridgeAdapter) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge
|
||||
contextContainer:_contextContainer];
|
||||
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;
|
||||
}
|
||||
|
||||
#pragma mark - New Arch Utilities
|
||||
|
||||
- (void)createReactHostIfNeeded
|
||||
{
|
||||
if (_reactHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
_reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL]
|
||||
hostDelegate:nil
|
||||
turboModuleManagerDelegate:_turboModuleManagerDelegate
|
||||
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
|
||||
return [weakSelf createJSRuntimeFactory];
|
||||
}];
|
||||
[_reactHost setBundleURLProvider:^NSURL *() {
|
||||
return [weakSelf bundleURL];
|
||||
}];
|
||||
[_reactHost setContextContainerHandler:self];
|
||||
[_reactHost start];
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
|
||||
{
|
||||
#if USE_HERMES
|
||||
return std::make_shared<facebook::react::RCTHermesInstance>(_reactNativeConfig, nullptr);
|
||||
#else
|
||||
return std::make_shared<facebook::react::RCTJscInstance>();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)didCreateContextContainer:(std::shared_ptr<facebook::react::ContextContainer>)contextContainer
|
||||
{
|
||||
contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [self bundleURL];
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return self->_configuration.bundleURL;
|
||||
}
|
||||
|
||||
@end
|
||||
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.
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
export * from '../../../src/private/specs/components/PopupMenuAndroidNativeComponent';
|
||||
import PopupMenuAndroidNativeComponent from '../../../src/private/specs/components/PopupMenuAndroidNativeComponent';
|
||||
export default PopupMenuAndroidNativeComponent;
|
||||
@@ -144,6 +144,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
borderTopLeftRadius: true,
|
||||
borderTopRightRadius: true,
|
||||
borderTopStartRadius: true,
|
||||
cursor: true,
|
||||
opacity: true,
|
||||
pointerEvents: true,
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ const version: $ReadOnly<{
|
||||
patch: number,
|
||||
prerelease: string | null,
|
||||
}> = {
|
||||
major: 1000,
|
||||
minor: 0,
|
||||
major: 0,
|
||||
minor: 74,
|
||||
patch: 0,
|
||||
prerelease: null,
|
||||
prerelease: 'rc.7',
|
||||
};
|
||||
|
||||
module.exports = {version};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
type Module = Object;
|
||||
type RegisterCallableModule = (
|
||||
name: string,
|
||||
moduleOrFactory: Module | (() => Module),
|
||||
) => void;
|
||||
|
||||
export const registerCallableModule: RegisterCallableModule;
|
||||
@@ -308,7 +308,6 @@ export type Props<ItemT> = {
|
||||
* Also inherits [ScrollView Props](docs/scrollview.html#props), unless it is nested in another FlatList of same orientation.
|
||||
*/
|
||||
class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
props: Props<ItemT>;
|
||||
/**
|
||||
* Scrolls to the end of the content. May be janky without `getItemLayout` prop.
|
||||
*/
|
||||
|
||||
@@ -1020,6 +1020,54 @@ Please follow the instructions at: fburl.com/rn-remote-assets`,
|
||||
});
|
||||
});
|
||||
|
||||
it('detects a component stack for ts, tsx, jsx, and js files', () => {
|
||||
expect(
|
||||
parseLogBoxLog([
|
||||
'Some kind of message\n in MyTSComponent (at MyTSXComponent.ts:1)\n in MyTSXComponent (at MyTSCComponent.tsx:1)\n in MyJSXComponent (at MyJSXComponent.jsx:1)\n in MyJSComponent (at MyJSComponent.js:1)',
|
||||
]),
|
||||
).toEqual({
|
||||
componentStack: [
|
||||
{
|
||||
content: 'MyTSComponent',
|
||||
fileName: 'MyTSXComponent.ts',
|
||||
location: {
|
||||
column: -1,
|
||||
row: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
content: 'MyTSXComponent',
|
||||
fileName: 'MyTSCComponent.tsx',
|
||||
location: {
|
||||
column: -1,
|
||||
row: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
content: 'MyJSXComponent',
|
||||
fileName: 'MyJSXComponent.jsx',
|
||||
location: {
|
||||
column: -1,
|
||||
row: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
content: 'MyJSComponent',
|
||||
fileName: 'MyJSComponent.js',
|
||||
location: {
|
||||
column: -1,
|
||||
row: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
category: 'Some kind of message',
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('detects a component stack in the first argument (JSC)', () => {
|
||||
expect(
|
||||
parseLogBoxLog([
|
||||
|
||||
@@ -192,7 +192,7 @@ export function parseComponentStack(message: string): ComponentStack {
|
||||
if (!s) {
|
||||
return null;
|
||||
}
|
||||
const match = s.match(/(.*) \(at (.*\.js):([\d]+)\)/);
|
||||
const match = s.match(/(.*) \(at (.*\.(?:js|jsx|ts|tsx)):([\d]+)\)/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import View from '../Components/View/View';
|
||||
import DebuggingOverlay from '../Debugging/DebuggingOverlay';
|
||||
import useSubscribeToDebuggingOverlayRegistry from '../Debugging/useSubscribeToDebuggingOverlayRegistry';
|
||||
import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter';
|
||||
import ReactDevToolsOverlay from '../Inspector/ReactDevToolsOverlay';
|
||||
import LogBoxNotificationContainer from '../LogBox/LogBoxNotificationContainer';
|
||||
import StyleSheet from '../StyleSheet/StyleSheet';
|
||||
import {RootTagContext, createRootTag} from './RootTag';
|
||||
@@ -64,6 +63,26 @@ const InspectorDeferred = ({
|
||||
);
|
||||
};
|
||||
|
||||
type ReactDevToolsOverlayDeferredProps = {
|
||||
inspectedViewRef: InspectedViewRef,
|
||||
reactDevToolsAgent: ReactDevToolsAgent,
|
||||
};
|
||||
|
||||
const ReactDevToolsOverlayDeferred = ({
|
||||
inspectedViewRef,
|
||||
reactDevToolsAgent,
|
||||
}: ReactDevToolsOverlayDeferredProps) => {
|
||||
const ReactDevToolsOverlay =
|
||||
require('../Inspector/ReactDevToolsOverlay').default;
|
||||
|
||||
return (
|
||||
<ReactDevToolsOverlay
|
||||
inspectedViewRef={inspectedViewRef}
|
||||
reactDevToolsAgent={reactDevToolsAgent}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AppContainer = ({
|
||||
children,
|
||||
fabric,
|
||||
@@ -155,7 +174,7 @@ const AppContainer = ({
|
||||
<DebuggingOverlay ref={debuggingOverlayRef} />
|
||||
|
||||
{reactDevToolsAgent != null && (
|
||||
<ReactDevToolsOverlay
|
||||
<ReactDevToolsOverlayDeferred
|
||||
inspectedViewRef={innerViewRef}
|
||||
reactDevToolsAgent={reactDevToolsAgent}
|
||||
/>
|
||||
|
||||
@@ -27,6 +27,8 @@ export type DimensionValue =
|
||||
type AnimatableNumericValue = number | Animated.AnimatedNode;
|
||||
type AnimatableStringValue = string | Animated.AnimatedNode;
|
||||
|
||||
export type CursorValue = 'auto' | 'pointer';
|
||||
|
||||
/**
|
||||
* Flex Prop Types
|
||||
* @see https://reactnative.dev/docs/flexbox
|
||||
@@ -274,6 +276,7 @@ export interface ViewStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle {
|
||||
* Controls whether the View can be the target of touch events.
|
||||
*/
|
||||
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined;
|
||||
cursor?: CursorValue | undefined;
|
||||
}
|
||||
|
||||
export type FontVariant =
|
||||
@@ -403,4 +406,5 @@ export interface ImageStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle {
|
||||
tintColor?: ColorValue | undefined;
|
||||
opacity?: AnimatableNumericValue | undefined;
|
||||
objectFit?: 'cover' | 'contain' | 'fill' | 'scale-down' | undefined;
|
||||
cursor?: CursorValue | undefined;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ export type EdgeInsetsValue = {
|
||||
export type DimensionValue = number | string | 'auto' | AnimatedNode | null;
|
||||
export type AnimatableNumericValue = number | AnimatedNode;
|
||||
|
||||
export type CursorValue = 'auto' | 'pointer';
|
||||
|
||||
/**
|
||||
* React Native's layout system is based on Flexbox and is powered both
|
||||
* on iOS and Android by an open source project called `Yoga`:
|
||||
@@ -729,6 +731,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
|
||||
opacity?: AnimatableNumericValue,
|
||||
elevation?: number,
|
||||
pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only',
|
||||
cursor?: CursorValue,
|
||||
}>;
|
||||
|
||||
export type ____ViewStyle_Internal = $ReadOnly<{
|
||||
|
||||
@@ -1773,41 +1773,6 @@ declare export default typeof NativeKeyboardObserver;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.android.js 1`] = `
|
||||
"export type PopupMenuAndroidInstance = {
|
||||
+show: () => void,
|
||||
};
|
||||
type Props = {
|
||||
menuItems: $ReadOnlyArray<string>,
|
||||
onSelectionChange: (number) => void,
|
||||
children: React.Node,
|
||||
instanceRef: RefObject<?PopupMenuAndroidInstance>,
|
||||
};
|
||||
declare export default function PopupMenuAndroid(Props): React.Node;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.js 1`] = `
|
||||
"export type PopupMenuAndroidInstance = {
|
||||
+show: () => void,
|
||||
};
|
||||
type Props = {
|
||||
menuItems: $ReadOnlyArray<string>,
|
||||
onSelectionChange: (number) => void,
|
||||
children: Node,
|
||||
instanceRef: RefObject<?PopupMenuAndroidInstance>,
|
||||
};
|
||||
declare function PopupMenuAndroid(props: Props): Node;
|
||||
declare export default typeof PopupMenuAndroid;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Components/PopupMenuAndroid/PopupMenuAndroidNativeComponent.js 1`] = `
|
||||
"export * from \\"../../../src/private/specs/components/PopupMenuAndroidNativeComponent\\";
|
||||
declare export default typeof PopupMenuAndroidNativeComponent;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Components/Pressable/Pressable.js 1`] = `
|
||||
"type ViewStyleProp = $ElementType<React.ElementConfig<typeof View>, \\"style\\">;
|
||||
export type StateCallbackType = $ReadOnly<{|
|
||||
@@ -7458,6 +7423,7 @@ export type EdgeInsetsValue = {
|
||||
};
|
||||
export type DimensionValue = number | string | \\"auto\\" | AnimatedNode | null;
|
||||
export type AnimatableNumericValue = number | AnimatedNode;
|
||||
export type CursorValue = \\"auto\\" | \\"pointer\\";
|
||||
type ____LayoutStyle_Internal = $ReadOnly<{
|
||||
display?: \\"none\\" | \\"flex\\",
|
||||
width?: DimensionValue,
|
||||
@@ -7608,6 +7574,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
|
||||
opacity?: AnimatableNumericValue,
|
||||
elevation?: number,
|
||||
pointerEvents?: \\"auto\\" | \\"none\\" | \\"box-none\\" | \\"box-only\\",
|
||||
cursor?: CursorValue,
|
||||
}>;
|
||||
export type ____ViewStyle_Internal = $ReadOnly<{
|
||||
...____ViewStyle_InternalCore,
|
||||
@@ -9026,7 +8993,6 @@ declare module.exports: {
|
||||
get ImageBackground(): ImageBackground,
|
||||
get InputAccessoryView(): InputAccessoryView,
|
||||
get KeyboardAvoidingView(): KeyboardAvoidingView,
|
||||
get PopupMenuAndroid(): PopupMenuAndroid,
|
||||
get Modal(): Modal,
|
||||
get Pressable(): Pressable,
|
||||
get ProgressBarAndroid(): ProgressBarAndroid,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#import <jsinspector-modern/ReactCdp.h>
|
||||
#endif
|
||||
|
||||
@interface RCTBridge (Inspector)
|
||||
|
||||
/**
|
||||
* The HostTarget for this bridge, if one has been created. Exposed for RCTCxxBridge only.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly)
|
||||
#ifdef __cplusplus
|
||||
facebook::react::jsinspector_modern::PageTarget *
|
||||
#else
|
||||
// The inspector infrastructure cannot be used in C or Swift code.
|
||||
void *
|
||||
#endif
|
||||
inspectorTarget;
|
||||
|
||||
@property (nonatomic, readonly, getter=isInspectable) BOOL inspectable;
|
||||
|
||||
@end
|
||||
@@ -6,9 +6,6 @@
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#ifdef __cplusplus
|
||||
#import <jsinspector-modern/ReactCdp.h>
|
||||
#endif
|
||||
|
||||
@class RCTModuleRegistry;
|
||||
@class RCTModuleData;
|
||||
@@ -73,17 +70,6 @@ RCT_EXTERN void RCTRegisterModule(Class);
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) RCTModuleRegistry *moduleRegistry;
|
||||
|
||||
/**
|
||||
* The page target for this bridge, if one has been created. Exposed for RCTCxxBridge only.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly)
|
||||
#ifdef __cplusplus
|
||||
facebook::react::jsinspector_modern::PageTarget *
|
||||
#else
|
||||
// The inspector infrastructure cannot be used in C code.
|
||||
void *
|
||||
#endif
|
||||
inspectorTarget;
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTCxxBridge)
|
||||
@@ -155,12 +141,6 @@ RCT_EXTERN void RCTRegisterModule(Class);
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (Inspector)
|
||||
|
||||
@property (nonatomic, readonly, getter=isInspectable) BOOL inspectable;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTCxxBridge : RCTBridge
|
||||
|
||||
// TODO(cjhopman): this seems unsafe unless we require that it is only called on the main js queue.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridge+Inspector.h"
|
||||
#import "RCTBridge+Private.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#import <ReactCommon/CallInvoker.h>
|
||||
#endif
|
||||
|
||||
#import "RCTBridgeProxy.h"
|
||||
|
||||
@interface RCTBridgeProxy (Cxx)
|
||||
|
||||
#ifdef __cplusplus
|
||||
@property (nonatomic, readwrite) std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker;
|
||||
#endif
|
||||
|
||||
@end
|
||||
@@ -15,6 +15,7 @@
|
||||
@class RCTViewRegistry;
|
||||
|
||||
@interface RCTBridgeProxy : NSProxy
|
||||
|
||||
- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
|
||||
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
@@ -34,4 +35,5 @@
|
||||
*/
|
||||
- (id)moduleForClass:(Class)moduleClass;
|
||||
- (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad;
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
*/
|
||||
|
||||
#import "RCTBridgeProxy.h"
|
||||
#import "RCTBridgeProxy+Cxx.h"
|
||||
|
||||
#import <React/RCTBridge+Private.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <ReactCommon/CallInvoker.h>
|
||||
#import <jsi/jsi.h>
|
||||
|
||||
using namespace facebook;
|
||||
@@ -21,6 +24,12 @@ using namespace facebook;
|
||||
- (void)forwardInvocation:(NSInvocation *)invocation;
|
||||
@end
|
||||
|
||||
@interface RCTBridgeProxy ()
|
||||
|
||||
@property (nonatomic, readwrite) std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridgeProxy {
|
||||
RCTUIManagerProxy *_uiManagerProxy;
|
||||
RCTModuleRegistry *_moduleRegistry;
|
||||
@@ -84,6 +93,12 @@ using namespace facebook;
|
||||
return _runtime;
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::CallInvoker>)jsCallInvoker
|
||||
{
|
||||
[self logWarning:@"Please migrate to RuntimeExecutor" cmd:_cmd];
|
||||
return _jsCallInvoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* RCTModuleRegistry
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#import <React/RCTAnimationType.h>
|
||||
#import <React/RCTBorderCurve.h>
|
||||
#import <React/RCTBorderStyle.h>
|
||||
#import <React/RCTCursor.h>
|
||||
#import <React/RCTDefines.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTPointerEvents.h>
|
||||
@@ -80,6 +81,8 @@ typedef NSURL RCTFileURL;
|
||||
+ (UIBarStyle)UIBarStyle:(id)json __deprecated;
|
||||
#endif
|
||||
|
||||
+ (RCTCursor)RCTCursor:(id)json;
|
||||
|
||||
+ (CGFloat)CGFloat:(id)json;
|
||||
+ (CGPoint)CGPoint:(id)json;
|
||||
+ (CGSize)CGSize:(id)json;
|
||||
|
||||
@@ -545,6 +545,15 @@ RCT_ENUM_CONVERTER(
|
||||
UIBarStyleDefault,
|
||||
integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(
|
||||
RCTCursor,
|
||||
(@{
|
||||
@"auto" : @(RCTCursorAuto),
|
||||
@"pointer" : @(RCTCursorPointer),
|
||||
}),
|
||||
RCTCursorAuto,
|
||||
integerValue)
|
||||
|
||||
static void convertCGStruct(const char *type, NSArray *fields, CGFloat *result, id json)
|
||||
{
|
||||
NSUInteger count = fields.count;
|
||||
|
||||
@@ -21,10 +21,10 @@ NSDictionary* RCTGetReactNativeVersion(void)
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^(void){
|
||||
__rnVersion = @{
|
||||
RCTVersionMajor: @(1000),
|
||||
RCTVersionMinor: @(0),
|
||||
RCTVersionMajor: @(0),
|
||||
RCTVersionMinor: @(74),
|
||||
RCTVersionPatch: @(0),
|
||||
RCTVersionPrerelease: [NSNull null],
|
||||
RCTVersionPrerelease: @"rc.7",
|
||||
};
|
||||
});
|
||||
return __rnVersion;
|
||||
|
||||
@@ -56,6 +56,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) RCTSurfaceHostingViewActivityIndicatorViewFactory activityIndicatorViewFactory;
|
||||
|
||||
/**
|
||||
* When set to `YES`, the activity indicator is not automatically hidden when the Surface stage changes.
|
||||
* In this scenario, users should invoke `hideActivityIndicator` to remove it.
|
||||
*
|
||||
* @param disabled: if `YES`, the auto-hide is disabled. Otherwise the loading view will be hidden automatically
|
||||
*/
|
||||
- (void)disableActivityIndicatorAutoHide:(BOOL)disabled;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
+17
-2
@@ -24,6 +24,7 @@
|
||||
UIView *_Nullable _activityIndicatorView;
|
||||
UIView *_Nullable _surfaceView;
|
||||
RCTSurfaceStage _stage;
|
||||
BOOL _autoHideDisabled;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-(instancetype)init)
|
||||
@@ -36,6 +37,7 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
|
||||
if (self = [super initWithFrame:CGRectZero]) {
|
||||
_surface = surface;
|
||||
_sizeMeasureMode = sizeMeasureMode;
|
||||
_autoHideDisabled = NO;
|
||||
|
||||
_surface.delegate = self;
|
||||
_stage = surface.stage;
|
||||
@@ -124,6 +126,10 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
|
||||
_sizeMeasureMode = sizeMeasureMode;
|
||||
[self _invalidateLayout];
|
||||
}
|
||||
- (void)disableActivityIndicatorAutoHide:(BOOL)disabled
|
||||
{
|
||||
_autoHideDisabled = disabled;
|
||||
}
|
||||
|
||||
#pragma mark - isActivityIndicatorViewVisible
|
||||
|
||||
@@ -162,7 +168,16 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
|
||||
_surfaceView = _surface.view;
|
||||
_surfaceView.frame = self.bounds;
|
||||
_surfaceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[self addSubview:_surfaceView];
|
||||
if (_activityIndicatorView && _autoHideDisabled) {
|
||||
// The activity indicator is still showing and the surface is set to
|
||||
// prevent the auto hide. This means that the application will take care of
|
||||
// hiding it when it's ready.
|
||||
// Let's add the surfaceView below the activity indicator so it's ready once
|
||||
// the activity indicator is hidden.
|
||||
[self insertSubview:_surfaceView belowSubview:_activityIndicatorView];
|
||||
} else {
|
||||
[self addSubview:_surfaceView];
|
||||
}
|
||||
} else {
|
||||
[_surfaceView removeFromSuperview];
|
||||
_surfaceView = nil;
|
||||
@@ -204,7 +219,7 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
|
||||
- (void)_updateViews
|
||||
{
|
||||
self.isSurfaceViewVisible = RCTSurfaceStageIsRunning(_stage);
|
||||
self.isActivityIndicatorViewVisible = RCTSurfaceStageIsPreparing(_stage);
|
||||
self.isActivityIndicatorViewVisible = _autoHideDisabled || RCTSurfaceStageIsPreparing(_stage);
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <React/RCTBridge+Inspector.h>
|
||||
#import <React/RCTBridge+Private.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTConstants.h>
|
||||
|
||||
@@ -89,6 +89,9 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if (_invalidated) {
|
||||
return;
|
||||
}
|
||||
_invalidated = YES;
|
||||
[self _cleanupObservers];
|
||||
}
|
||||
@@ -109,10 +112,7 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RCTWindowFrameDidChangeNotification object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(invalidate)
|
||||
name:RCTBridgeWillInvalidateModulesNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RCTBridgeWillInvalidateModulesNotification object:nil];
|
||||
}
|
||||
|
||||
static BOOL RCTIsIPhoneNotched()
|
||||
|
||||
@@ -274,7 +274,13 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[_actionDelegate reloadFromRedBoxController:self];
|
||||
if (_actionDelegate != nil) {
|
||||
[_actionDelegate reloadFromRedBoxController:self];
|
||||
} else {
|
||||
// In bridgeless mode `RCTRedBox` gets deallocated, we need to notify listeners anyway.
|
||||
RCTTriggerReloadCommandListeners(@"Redbox");
|
||||
[self dismiss];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showExtraDataViewController
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <future>
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBridge+Inspector.h>
|
||||
#import <React/RCTBridge+Private.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBridgeMethod.h>
|
||||
|
||||
+32
@@ -257,6 +257,11 @@ using namespace facebook::react;
|
||||
self.layer.doubleSided = newViewProps.backfaceVisibility == BackfaceVisibility::Visible;
|
||||
}
|
||||
|
||||
// `cursor`
|
||||
if (oldViewProps.cursor != newViewProps.cursor) {
|
||||
needsInvalidateLayer = YES;
|
||||
}
|
||||
|
||||
// `shouldRasterize`
|
||||
if (oldViewProps.shouldRasterize != newViewProps.shouldRasterize) {
|
||||
self.layer.shouldRasterize = newViewProps.shouldRasterize;
|
||||
@@ -592,6 +597,33 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
|
||||
layer.shadowPath = nil;
|
||||
}
|
||||
|
||||
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */
|
||||
// Stage 1.5. Cursor / Hover Effects
|
||||
if (@available(iOS 17.0, *)) {
|
||||
UIHoverStyle *hoverStyle = nil;
|
||||
if (_props->cursor == Cursor::Pointer) {
|
||||
const RCTCornerInsets cornerInsets =
|
||||
RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero);
|
||||
#if TARGET_OS_IOS
|
||||
// Due to an Apple bug, it seems on iOS, UIShapes made with `[UIShape shapeWithBezierPath:]`
|
||||
// evaluate their shape on the superviews' coordinate space. This leads to the hover shape
|
||||
// rendering incorrectly on iOS, iOS apps in compatibility mode on visionOS, but not on visionOS.
|
||||
// To work around this, for iOS, we can calculate the border path based on `view.frame` (the
|
||||
// superview's coordinate space) instead of view.bounds.
|
||||
CGPathRef borderPath = RCTPathCreateWithRoundedRect(self.frame, cornerInsets, NULL);
|
||||
#else // TARGET_OS_VISION
|
||||
CGPathRef borderPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, NULL);
|
||||
#endif
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:borderPath];
|
||||
CGPathRelease(borderPath);
|
||||
UIShape *shape = [UIShape shapeWithBezierPath:bezierPath];
|
||||
|
||||
hoverStyle = [UIHoverStyle styleWithEffect:[UIHoverAutomaticEffect effect] shape:shape];
|
||||
}
|
||||
[self setHoverStyle:hoverStyle];
|
||||
}
|
||||
#endif
|
||||
|
||||
// Stage 2. Border Rendering
|
||||
const bool useCoreAnimationBorderRendering =
|
||||
borderMetrics.borderColors.isUniform() && borderMetrics.borderWidths.isUniform() &&
|
||||
|
||||
@@ -1680,6 +1680,16 @@ static UIView *_jsResponder;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)count
|
||||
{
|
||||
return self->_registry.count;
|
||||
}
|
||||
|
||||
- (NSEnumerator *)keyEnumerator
|
||||
{
|
||||
return self->_registry.keyEnumerator;
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)key
|
||||
{
|
||||
if (![key isKindOfClass:[NSNumber class]]) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTCursor) {
|
||||
RCTCursorAuto,
|
||||
RCTCursorPointer,
|
||||
};
|
||||
@@ -10,6 +10,7 @@
|
||||
#import <React/RCTBorderCurve.h>
|
||||
#import <React/RCTBorderStyle.h>
|
||||
#import <React/RCTComponent.h>
|
||||
#import <React/RCTCursor.h>
|
||||
#import <React/RCTPointerEvents.h>
|
||||
|
||||
extern const UIAccessibilityTraits SwitchAccessibilityTrait;
|
||||
@@ -120,6 +121,8 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
|
||||
*/
|
||||
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
|
||||
|
||||
@property (nonatomic, assign) RCTCursor cursor;
|
||||
|
||||
/**
|
||||
* (Experimental and unused for Paper) Pointer event handlers.
|
||||
*/
|
||||
|
||||
@@ -136,6 +136,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
_borderCurve = RCTBorderCurveCircular;
|
||||
_borderStyle = RCTBorderStyleSolid;
|
||||
_hitTestEdgeInsets = UIEdgeInsetsZero;
|
||||
_cursor = RCTCursorAuto;
|
||||
|
||||
_backgroundColor = super.backgroundColor;
|
||||
}
|
||||
@@ -796,6 +797,8 @@ static CGFloat RCTDefaultIfNegativeTo(CGFloat defaultValue, CGFloat x)
|
||||
|
||||
RCTUpdateShadowPathForView(self);
|
||||
|
||||
RCTUpdateHoverStyleForView(self);
|
||||
|
||||
const RCTCornerRadii cornerRadii = [self cornerRadii];
|
||||
const UIEdgeInsets borderInsets = [self bordersAsInsets];
|
||||
const RCTBorderColors borderColors = [self borderColorsWithTraitCollection:self.traitCollection];
|
||||
@@ -891,6 +894,33 @@ static void RCTUpdateShadowPathForView(RCTView *view)
|
||||
}
|
||||
}
|
||||
|
||||
static void RCTUpdateHoverStyleForView(RCTView *view)
|
||||
{
|
||||
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */
|
||||
if (@available(iOS 17.0, *)) {
|
||||
UIHoverStyle *hoverStyle = nil;
|
||||
if ([view cursor] == RCTCursorPointer) {
|
||||
const RCTCornerRadii cornerRadii = [view cornerRadii];
|
||||
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero);
|
||||
#if TARGET_OS_IOS
|
||||
// Due to an Apple bug, it seems on iOS, `[UIShape shapeWithBezierPath:]` needs to
|
||||
// be calculated in the superviews' coordinate space (view.frame). This is not true
|
||||
// on other platforms like visionOS.
|
||||
CGPathRef borderPath = RCTPathCreateWithRoundedRect(view.frame, cornerInsets, NULL);
|
||||
#else // TARGET_OS_VISION
|
||||
CGPathRef borderPath = RCTPathCreateWithRoundedRect(view.bounds, cornerInsets, NULL);
|
||||
#endif
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:borderPath];
|
||||
CGPathRelease(borderPath);
|
||||
UIShape *shape = [UIShape shapeWithBezierPath:bezierPath];
|
||||
|
||||
hoverStyle = [UIHoverStyle styleWithEffect:[UIHoverHighlightEffect effect] shape:shape];
|
||||
}
|
||||
[view setHoverStyle:hoverStyle];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)updateClippingForLayer:(CALayer *)layer
|
||||
{
|
||||
CALayer *mask = nil;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert+Transform.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTCursor.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTUIManager.h"
|
||||
@@ -195,6 +196,7 @@ RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifie
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
|
||||
RCT_EXPORT_VIEW_PROPERTY(cursor, RCTCursor)
|
||||
RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user