diff --git a/.circleci/config.yml b/.circleci/config.yml index 94fed2af278951..a642711e33d1af 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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 diff --git a/.circleci/configurations/commands.yml b/.circleci/configurations/commands.yml index bce934a6b42119..bba0dc1a635bf1 100644 --- a/.circleci/configurations/commands.yml +++ b/.circleci/configurations/commands.yml @@ -31,7 +31,7 @@ commands: - restore_cache: key: *rbenv_cache_key - run: - name: Bundle Install + name: Install the proper Ruby and run Bundle install command: | # Check if rbenv is installed. CircleCI is migrating to rbenv so we may not need to always install it. @@ -58,8 +58,10 @@ commands: # Set ruby dependencies rbenv global << parameters.ruby_version >> if [[ << parameters.ruby_version >> == "2.6.10" ]]; then + rbenv rehash gem install bundler -v 2.4.22 else + rbenv rehash gem install bundler fi bundle check || bundle install --path vendor/bundle --clean diff --git a/.circleci/configurations/executors.yml b/.circleci/configurations/executors.yml index 1c4c6a9bfb806c..f06f0497773e35 100644 --- a/.circleci/configurations/executors.yml +++ b/.circleci/configurations/executors.yml @@ -33,13 +33,13 @@ executors: <<: *defaults macos: xcode: *xcode_version - resource_class: macos.x86.medium.gen2 + resource_class: macos.m1.medium.gen1 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 + resource_class: macos.m1.medium.gen1 environment: - - BUILD_FROM_SOURCE: true + - RCT_BUILD_HERMES_FROM_SOURCE: true diff --git a/.circleci/configurations/jobs.yml b/.circleci/configurations/jobs.yml index 39eb852857d3dd..d269b56320a70d 100644 --- a/.circleci/configurations/jobs.yml +++ b/.circleci/configurations/jobs.yml @@ -116,7 +116,7 @@ jobs: executor: reactnativeios parameters: ruby_version: - default: "2.7.7" + default: "2.7.8" description: The version of ruby that must be used type: string steps: @@ -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: diff --git a/.circleci/configurations/test_workflows/testAll.yml b/.circleci/configurations/test_workflows/testAll.yml index 9525846607ad7b..1ea35b2579c17b 100644 --- a/.circleci/configurations/test_workflows/testAll.yml +++ b/.circleci/configurations/test_workflows/testAll.yml @@ -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" @@ -56,8 +57,8 @@ - test_ios_template: requires: - build_npm_package - name: "Test Template with Ruby 3.2.0" - ruby_version: "3.2.0" + name: "Test Template with Ruby 3.2.2" + ruby_version: "3.2.2" architecture: "NewArch" flavor: "Debug" executor: reactnativeios-lts @@ -71,15 +72,15 @@ jsengine: ["Hermes", "JSC"] use_frameworks: ["StaticLibraries", "DynamicFrameworks"] exclude: - # This config is tested with Ruby 3.2.0. Let's not double test it. + # This config is tested with Ruby 3.2.2. Let's not double test it. - flavor: "Debug" jsengine: "Hermes" use_frameworks: "StaticLibraries" - test_ios_rntester: requires: - build_hermes_macos - name: "Test RNTester with Ruby 3.2.0" - ruby_version: "3.2.0" + name: "Test RNTester with Ruby 3.2.2" + ruby_version: "3.2.2" executor: reactnativeios-lts - test_ios_rntester: requires: @@ -95,7 +96,7 @@ # Tested by test_ios-JSC - jsengine: "JSC" use_frameworks: "StaticLibraries" - # Tested with Ruby 3.2.0, do not test this twice. + # Tested with Ruby 3.2.2, do not test this twice. - jsengine: "Hermes" use_frameworks: "StaticLibraries" - test_ios_rntester: diff --git a/.circleci/configurations/test_workflows/testAndroid.yml b/.circleci/configurations/test_workflows/testAndroid.yml index 08f925eca37b61..83a1d0e36c99c9 100644 --- a/.circleci/configurations/test_workflows/testAndroid.yml +++ b/.circleci/configurations/test_workflows/testAndroid.yml @@ -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" diff --git a/.circleci/configurations/test_workflows/testE2E.yml b/.circleci/configurations/test_workflows/testE2E.yml index 998abf796ffe8c..a926a395775c0e 100644 --- a/.circleci/configurations/test_workflows/testE2E.yml +++ b/.circleci/configurations/test_workflows/testE2E.yml @@ -5,5 +5,5 @@ - equal: [ false, << pipeline.parameters.run_nightly_workflow >> ] jobs: - test_e2e_ios: - ruby_version: "2.7.7" + ruby_version: "2.7.8" - test_e2e_android diff --git a/.circleci/configurations/test_workflows/testIOS.yml b/.circleci/configurations/test_workflows/testIOS.yml index a1e6c479cfe571..4a662f10dadcac 100644 --- a/.circleci/configurations/test_workflows/testIOS.yml +++ b/.circleci/configurations/test_workflows/testIOS.yml @@ -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" @@ -47,8 +48,8 @@ - test_ios_template: requires: - build_npm_package - name: "Test Template with Ruby 3.2.0" - ruby_version: "3.2.0" + name: "Test Template with Ruby 3.2.2" + ruby_version: "3.2.2" architecture: "NewArch" flavor: "Debug" executor: reactnativeios-lts @@ -62,15 +63,15 @@ jsengine: ["Hermes", "JSC"] use_frameworks: ["StaticLibraries", "DynamicFrameworks"] exclude: - # Tested with Ruby 3.2.0, let's not double test this + # Tested with Ruby 3.2.2, let's not double test this - flavor: "Debug" jsengine: "Hermes" use_frameworks: "StaticLibraries" - test_ios_rntester: requires: - build_hermes_macos - name: "RNTester on Ruby 3.2.0" - ruby_version: "3.2.0" + name: "RNTester on Ruby 3.2.2" + ruby_version: "3.2.2" executor: reactnativeios-lts - test_ios_rntester: name: "RNTester with Dynamic Frameworks" diff --git a/.circleci/configurations/top_level.yml b/.circleci/configurations/top_level.yml index 5d3281d36400a6..4ff9d802e8fa19 100644 --- a/.circleci/configurations/top_level.yml +++ b/.circleci/configurations/top_level.yml @@ -71,32 +71,32 @@ references: cache_keys: checkout_cache_key: &checkout_cache_key v1-checkout - gems_cache_key: &gems_cache_key v1-gems-{{ checksum "Gemfile.lock" }} + gems_cache_key: &gems_cache_key v1-gems-{{ arch }}-{{ checksum "Gemfile.lock" }} gradle_cache_key: &gradle_cache_key v3-gradle-{{ .Environment.CIRCLE_JOB }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "packages/react-native/ReactAndroid/gradle.properties" }} yarn_cache_key: &yarn_cache_key v6-yarn-cache-{{ .Environment.CIRCLE_JOB }} - rbenv_cache_key: &rbenv_cache_key v1-rbenv-{{ checksum "/tmp/required_ruby" }} + rbenv_cache_key: &rbenv_cache_key v1-rbenv-{{ arch }}-{{ checksum "/tmp/required_ruby" }} hermes_workspace_cache_key: &hermes_workspace_cache_key v5-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/hermes/hermesversion" }} hermes_workspace_debug_cache_key: &hermes_workspace_debug_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }} hermes_workspace_release_cache_key: &hermes_workspace_release_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }} hermes_linux_cache_key: &hermes_linux_cache_key v1-hermes-{{ .Environment.CIRCLE_JOB }}-linux-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} 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_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_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" }} + hermesc_apple_cache_key: &hermesc_apple_cache_key v4-hermesc-apple-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_apple_slices_cache_key: &hermes_apple_slices_cache_key v8-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 v6-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 v5-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 v5-hermes-release-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_macosx_bin_debug_cache_key: &hermes_macosx_bin_debug_cache_key v3-hermes-debug-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_dsym_debug_cache_key: &hermes_dsym_debug_cache_key v3-hermes-debug-dsym-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_dsym_release_cache_key: &hermes_dsym_release_cache_key v3-hermes-release-dsym-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} # Cocoapods - RNTester - pods_cache_key: &pods_cache_key v11-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }} - cocoapods_cache_key: &cocoapods_cache_key v11-cocoapods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock" }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/hermes/hermesversion" }} - rntester_podfile_lock_cache_key: &rntester_podfile_lock_cache_key v9-podfilelock-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/week_year" }}-{{ checksum "/tmp/hermes/hermesversion" }} + pods_cache_key: &pods_cache_key v12-pods-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }} + cocoapods_cache_key: &cocoapods_cache_key v12-cocoapods-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock" }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/hermes/hermesversion" }} + rntester_podfile_lock_cache_key: &rntester_podfile_lock_cache_key v10-podfilelock-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/week_year" }}-{{ checksum "/tmp/hermes/hermesversion" }} # Cocoapods - Template - template_cocoapods_cache_key: &template_cocoapods_cache_key v6-cocoapods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile.lock" }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }}-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "packages/rn-tester/Podfile.lock" }} - template_podfile_lock_cache_key: &template_podfile_lock_cache_key v6-podfilelock-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }}-{{ checksum "/tmp/week_year" }}-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "packages/rn-tester/Podfile.lock" }} + template_cocoapods_cache_key: &template_cocoapods_cache_key v6-cocoapods-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile.lock" }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }}-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "packages/rn-tester/Podfile.lock" }} + template_podfile_lock_cache_key: &template_podfile_lock_cache_key v6-podfilelock-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }}-{{ checksum "/tmp/week_year" }}-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "packages/rn-tester/Podfile.lock" }} cache_paths: hermes_workspace_macos_cache_paths: &hermes_workspace_macos_cache_paths @@ -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 diff --git a/.circleci/configurations/workflows.yml b/.circleci/configurations/workflows.yml index e5c085ee4ce08a..cccab425b0bb3c 100644 --- a/.circleci/configurations/workflows.yml +++ b/.circleci/configurations/workflows.yml @@ -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: diff --git a/.gitignore b/.gitignore index 7e334430180887..626d954cfa3f18 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/flow-typed/npm/chrome-launcher_v0.15.x.js b/flow-typed/npm/chrome-launcher_v0.15.x.js index 8f18f971b5cdf8..e520c5716c784c 100644 --- a/flow-typed/npm/chrome-launcher_v0.15.x.js +++ b/flow-typed/npm/chrome-launcher_v0.15.x.js @@ -44,6 +44,9 @@ declare module 'chrome-launcher' { declare class Launcher { getChromePath(): string; launch(options: Options): Promise; + Launcher: { + defaultFlags(): Array, + }; } declare module.exports: Launcher; diff --git a/package.json b/package.json index ca7b08196d9d51..134d9f8045a888 100644 --- a/package.json +++ b/package.json @@ -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,11 +58,11 @@ "@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.84", + "@react-native/metro-config": "0.74.84", "@tsconfig/node18": "1.0.1", - "@types/react": "^18.0.18", - "@typescript-eslint/parser": "^6.7.4", + "@types/react": "^18.2.6", + "@typescript-eslint/parser": "^7.1.1", "ansi-styles": "^4.2.1", "async": "^3.2.2", "babel-plugin-minify-dead-code-elimination": "^0.5.2", @@ -78,7 +77,7 @@ "eslint-plugin-babel": "^5.3.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-ft-flow": "^2.0.1", - "eslint-plugin-jest": "^26.5.3", + "eslint-plugin-jest": "^27.9.0", "eslint-plugin-jsx-a11y": "^6.6.0", "eslint-plugin-lint": "^1.0.0", "eslint-plugin-prettier": "^4.2.1", diff --git a/packages/assets/package.json b/packages/assets/package.json index 616930179d1867..75aa3d1c537977 100644 --- a/packages/assets/package.json +++ b/packages/assets/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/assets-registry", - "version": "0.74.0", + "version": "0.74.84", "description": "Asset support code for React Native.", "license": "MIT", "repository": { diff --git a/packages/babel-plugin-codegen/package.json b/packages/babel-plugin-codegen/package.json index ca3392302e261e..cdce7e67d22477 100644 --- a/packages/babel-plugin-codegen/package.json +++ b/packages/babel-plugin-codegen/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/babel-plugin-codegen", - "version": "0.74.0", + "version": "0.74.84", "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.84" }, "devDependencies": { "@babel/core": "^7.20.0" diff --git a/packages/community-cli-plugin/package.json b/packages/community-cli-plugin/package.json index 5d2f05cb845fb7..1697aa95ff4ed7 100644 --- a/packages/community-cli-plugin/package.json +++ b/packages/community-cli-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/community-cli-plugin", - "version": "0.74.0", + "version": "0.74.84", "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.9", + "@react-native-community/cli-tools": "13.6.9", + "@react-native/dev-middleware": "0.74.84", + "@react-native/metro-babel-transformer": "0.74.84", "chalk": "^4.0.0", "execa": "^5.1.1", "metro": "^0.80.3", diff --git a/packages/debugger-frontend/package.json b/packages/debugger-frontend/package.json index 4c100152f8edbf..6f03569daf9070 100644 --- a/packages/debugger-frontend/package.json +++ b/packages/debugger-frontend/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/debugger-frontend", - "version": "0.74.0", + "version": "0.74.84", "description": "Debugger frontend for React Native based on Chrome DevTools", "keywords": [ "react-native", diff --git a/packages/dev-middleware/package.json b/packages/dev-middleware/package.json index f1700a2fcf8760..3163fd968c8a7d 100644 --- a/packages/dev-middleware/package.json +++ b/packages/dev-middleware/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/dev-middleware", - "version": "0.74.0", + "version": "0.74.84", "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.84", "@rnx-kit/chromium-edge-launcher": "^1.0.0", "chrome-launcher": "^0.15.2", "connect": "^3.6.5", diff --git a/packages/dev-middleware/src/__tests__/InspectorProxyCustomMessageHandler-test.js b/packages/dev-middleware/src/__tests__/InspectorProxyCustomMessageHandler-test.js new file mode 100644 index 00000000000000..92c0eab475b372 --- /dev/null +++ b/packages/dev-middleware/src/__tests__/InspectorProxyCustomMessageHandler-test.js @@ -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 { + return new Promise(resolve => server.close(() => resolve())); +} diff --git a/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js b/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js index e601f219f06324..e5501b8e3ceb66 100644 --- a/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js +++ b/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js @@ -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( + `${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(); + } + }); }); }); diff --git a/packages/dev-middleware/src/createDevMiddleware.js b/packages/dev-middleware/src/createDevMiddleware.js index e6c9dc1f7d93e2..8766811b8885ba 100644 --- a/packages/dev-middleware/src/createDevMiddleware.js +++ b/packages/dev-middleware/src/createDevMiddleware.js @@ -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, + 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() diff --git a/packages/dev-middleware/src/index.flow.js b/packages/dev-middleware/src/index.flow.js index c662838fea10ce..7afbbdbd8894b3 100644 --- a/packages/dev-middleware/src/index.flow.js +++ b/packages/dev-middleware/src/index.flow.js @@ -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'; diff --git a/packages/dev-middleware/src/inspector-proxy/CustomMessageHandler.js b/packages/dev-middleware/src/inspector-proxy/CustomMessageHandler.js new file mode 100644 index 00000000000000..1c3dd473b7c118 --- /dev/null +++ b/packages/dev-middleware/src/inspector-proxy/CustomMessageHandler.js @@ -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; +} diff --git a/packages/dev-middleware/src/inspector-proxy/Device.js b/packages/dev-middleware/src/inspector-proxy/Device.js index 26befc0b1185c4..b4ef0ad731cad8 100644 --- a/packages/dev-middleware/src/inspector-proxy/Device.js +++ b/packages/dev-middleware/src/inspector-proxy/Device.js @@ -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; // 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; + // 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 { 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 { - 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) { diff --git a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js index 066fa44cc3d536..9b9916dc220d98 100644 --- a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js +++ b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js @@ -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 { @@ -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); diff --git a/packages/dev-middleware/src/inspector-proxy/types.js b/packages/dev-middleware/src/inspector-proxy/types.js index f4994a812c4830..a89bfc9a914d57 100644 --- a/packages/dev-middleware/src/inspector-proxy/types.js +++ b/packages/dev-middleware/src/inspector-proxy/types.js @@ -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 diff --git a/packages/dev-middleware/src/middleware/deprecated_openFlipperMiddleware.js b/packages/dev-middleware/src/middleware/deprecated_openFlipperMiddleware.js index 6a2d07a5f0374d..da6c6bc5c25bda 100644 --- a/packages/dev-middleware/src/middleware/deprecated_openFlipperMiddleware.js +++ b/packages/dev-middleware/src/middleware/deprecated_openFlipperMiddleware.js @@ -46,6 +46,14 @@ export default function deprecated_openFlipperMiddleware({ 'Flipper to be installed on your system to handle the ' + "'flipper://' URL scheme.", ); + logger?.info( + 'In React Native 0.74, Flipper is no longer included for new React ' + + 'Native projects. The Flipper React Native plugin is also ' + + 'unsupported. You can continue to use Flipper to debug ' + + "your app's JavaScript code, however we recommend switching to " + + 'a modern alternative.\nSee ' + + 'https://reactnative.dev/docs/debugging#opening-the-debugger.', + ); await open(FLIPPER_SELF_CONNECT_URL); res.end(); } catch (e) { diff --git a/packages/dev-middleware/src/utils/DefaultBrowserLauncher.js b/packages/dev-middleware/src/utils/DefaultBrowserLauncher.js index 8992f194c54192..9438d3f07408b9 100644 --- a/packages/dev-middleware/src/utils/DefaultBrowserLauncher.js +++ b/packages/dev-middleware/src/utils/DefaultBrowserLauncher.js @@ -51,12 +51,23 @@ const DefaultBrowserLauncher: BrowserLauncher = { `react-native-debugger-frontend-${browserType}`, ); const launchedChrome = await ChromeLauncher.launch({ - chromePath, chromeFlags: [ + ...ChromeLauncher.Launcher.defaultFlags().filter( + /** + * This flag controls whether Chrome treats a visually covered (occluded) tab + * as "backgrounded". We launch CDT as a single tab/window via `--app`, so we + * do want Chrome to treat our tab as "backgrounded" when the UI is covered. + * Omitting this flag allows "visibilitychange" events to fire properly. + */ + flag => flag !== '--disable-backgrounding-occluded-windows', + ), `--app=${url}`, `--user-data-dir=${userDataDir}`, '--window-size=1200,600', + '--guest', ], + chromePath, + ignoreDefaultFlags: true, }); return { diff --git a/packages/eslint-config-react-native/package.json b/packages/eslint-config-react-native/package.json index 7edb68a00afbe1..9f350c8337617a 100644 --- a/packages/eslint-config-react-native/package.json +++ b/packages/eslint-config-react-native/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/eslint-config", - "version": "0.74.0", + "version": "0.74.84", "description": "ESLint config for React Native", "license": "MIT", "repository": { @@ -22,13 +22,13 @@ "dependencies": { "@babel/core": "^7.20.0", "@babel/eslint-parser": "^7.20.0", - "@react-native/eslint-plugin": "0.74.0", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", + "@react-native/eslint-plugin": "0.74.84", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", "eslint-config-prettier": "^8.5.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-ft-flow": "^2.0.1", - "eslint-plugin-jest": "^26.5.3", + "eslint-plugin-jest": "^27.9.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", diff --git a/packages/eslint-plugin-react-native/package.json b/packages/eslint-plugin-react-native/package.json index acb35918dd30e1..0202dace517199 100644 --- a/packages/eslint-plugin-react-native/package.json +++ b/packages/eslint-plugin-react-native/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/eslint-plugin", - "version": "0.74.0", + "version": "0.74.84", "description": "ESLint rules for @react-native/eslint-config", "license": "MIT", "repository": { diff --git a/packages/eslint-plugin-specs/package.json b/packages/eslint-plugin-specs/package.json index fa04efd48a5717..c910bd83b4542d 100644 --- a/packages/eslint-plugin-specs/package.json +++ b/packages/eslint-plugin-specs/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/eslint-plugin-specs", - "version": "0.74.0", + "version": "0.74.84", "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.84", "make-dir": "^2.1.0", "pirates": "^4.0.1", "source-map-support": "0.5.0" diff --git a/packages/hermes-inspector-msggen/package.json b/packages/hermes-inspector-msggen/package.json index 0124f83b5419aa..517f24d39a882f 100644 --- a/packages/hermes-inspector-msggen/package.json +++ b/packages/hermes-inspector-msggen/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/hermes-inspector-msggen", - "version": "0.72.0", + "version": "0.74.84", "private": true, "description": "Hermes Inspector Message Generator for React Native", "license": "MIT", diff --git a/packages/metro-config/package.json b/packages/metro-config/package.json index 410f04a50b8952..026e3c12dfcc59 100644 --- a/packages/metro-config/package.json +++ b/packages/metro-config/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/metro-config", - "version": "0.74.0", + "version": "0.74.84", "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.84", + "@react-native/metro-babel-transformer": "0.74.84", "metro-config": "^0.80.3", "metro-runtime": "^0.80.3" } diff --git a/packages/normalize-color/package.json b/packages/normalize-color/package.json index c489d39b87bc80..25305edc0e95f2 100644 --- a/packages/normalize-color/package.json +++ b/packages/normalize-color/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/normalize-colors", - "version": "0.74.1", + "version": "0.74.84", "description": "Color normalization for React Native.", "license": "MIT", "repository": { diff --git a/packages/polyfills/package.json b/packages/polyfills/package.json index cadebf8191e007..3d3dc47eb48d58 100644 --- a/packages/polyfills/package.json +++ b/packages/polyfills/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/js-polyfills", - "version": "0.74.0", + "version": "0.74.84", "description": "Polyfills for React Native.", "license": "MIT", "repository": { diff --git a/packages/react-native-babel-preset/package.json b/packages/react-native-babel-preset/package.json index 74a6f1df7c917d..a3c4704c827710 100644 --- a/packages/react-native-babel-preset/package.json +++ b/packages/react-native-babel-preset/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/babel-preset", - "version": "0.74.0", + "version": "0.74.84", "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.84", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" }, diff --git a/packages/react-native-babel-transformer/package.json b/packages/react-native-babel-transformer/package.json index b78d79c643db84..98fefceed31117 100644 --- a/packages/react-native-babel-transformer/package.json +++ b/packages/react-native-babel-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/metro-babel-transformer", - "version": "0.74.0", + "version": "0.74.84", "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.84", "hermes-parser": "0.19.1", "nullthrows": "^1.1.1" }, diff --git a/packages/react-native-bots/package.json b/packages/react-native-bots/package.json index 961bdbd9d6bfab..129aea504dddbc 100644 --- a/packages/react-native-bots/package.json +++ b/packages/react-native-bots/package.json @@ -1,7 +1,7 @@ { "name": "@react-native/bots", "description": "React Native Bots", - "version": "0.0.0", + "version": "0.74.84", "private": true, "license": "MIT", "repository": { diff --git a/packages/react-native-codegen-typescript-test/package.json b/packages/react-native-codegen-typescript-test/package.json index c170d4cedccc8e..d01ddfe372fc5b 100644 --- a/packages/react-native-codegen-typescript-test/package.json +++ b/packages/react-native-codegen-typescript-test/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/codegen-typescript-test", - "version": "0.0.1", + "version": "0.74.84", "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.84" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsH-test.js.snap b/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsH-test.js.snap index aaf1c130eafba2..50a2d4dbae2877 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsH-test.js.snap +++ b/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsH-test.js.snap @@ -82,7 +82,7 @@ static inline std::string toString(const ArrayPropsNativeComponentViewSizesMask return result; } struct ArrayPropsNativeComponentViewObjectStruct { - std::string prop; + std::string prop{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentViewObjectStruct &result) { @@ -109,8 +109,8 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu struct ArrayPropsNativeComponentViewArrayOfObjectsStruct { - Float prop1; - int prop2; + Float prop1{0.0}; + int prop2{0}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentViewArrayOfObjectsStruct &result) { @@ -756,12 +756,12 @@ static inline std::string toString(const ObjectPropsNativeComponentIntEnumProp & } } struct ObjectPropsNativeComponentObjectPropStruct { - std::string stringProp; - bool booleanProp; - Float floatProp; - int intProp; - ObjectPropsNativeComponentStringEnumProp stringEnumProp; - ObjectPropsNativeComponentIntEnumProp intEnumProp; + std::string stringProp{\\"\\"}; + bool booleanProp{false}; + Float floatProp{0.0}; + int intProp{0}; + ObjectPropsNativeComponentStringEnumProp stringEnumProp{ObjectPropsNativeComponentStringEnumProp::Small}; + ObjectPropsNativeComponentIntEnumProp intEnumProp{ObjectPropsNativeComponentIntEnumProp::IntEnumProp0}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsNativeComponentObjectPropStruct &result) { @@ -798,7 +798,7 @@ static inline std::string toString(const ObjectPropsNativeComponentObjectPropStr } struct ObjectPropsNativeComponentObjectArrayPropStruct { - std::vector array; + std::vector array{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsNativeComponentObjectArrayPropStruct &result) { @@ -815,9 +815,9 @@ static inline std::string toString(const ObjectPropsNativeComponentObjectArrayPr } struct ObjectPropsNativeComponentObjectPrimitiveRequiredPropStruct { - ImageSource image; - SharedColor color; - Point point; + ImageSource image{}; + SharedColor color{}; + Point point{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsNativeComponentObjectPrimitiveRequiredPropStruct &result) { diff --git a/packages/react-native-codegen/package.json b/packages/react-native-codegen/package.json index 2e8200e957f0a7..2f1851f51c9f51 100644 --- a/packages/react-native-codegen/package.json +++ b/packages/react-native-codegen/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/codegen", - "version": "0.74.0", + "version": "0.74.84", "description": "Code generation tools for React Native", "license": "MIT", "repository": { diff --git a/packages/react-native-codegen/src/generators/components/CppHelpers.js b/packages/react-native-codegen/src/generators/components/CppHelpers.js index 9f8fd10387507f..d1bf2f61599aa4 100644 --- a/packages/react-native-codegen/src/generators/components/CppHelpers.js +++ b/packages/react-native-codegen/src/generators/components/CppHelpers.js @@ -166,6 +166,14 @@ function getEnumMaskName(enumName: string): string { return `${enumName}Mask`; } +function getDefaultInitializerString( + componentName: string, + prop: NamedShape, +): string { + const defaultValue = convertDefaultTypeToString(componentName, prop); + return `{${defaultValue}}`; +} + function convertDefaultTypeToString( componentName: string, prop: NamedShape, @@ -270,6 +278,7 @@ const IncludeTemplate = ({ }; module.exports = { + getDefaultInitializerString, convertDefaultTypeToString, getCppArrayTypeForAnnotation, getCppTypeForAnnotation, diff --git a/packages/react-native-codegen/src/generators/components/GeneratePropsH.js b/packages/react-native-codegen/src/generators/components/GeneratePropsH.js index da7da65f3a992e..e8af5043554695 100644 --- a/packages/react-native-codegen/src/generators/components/GeneratePropsH.js +++ b/packages/react-native-codegen/src/generators/components/GeneratePropsH.js @@ -23,8 +23,8 @@ const { getNativeTypeFromAnnotation, } = require('./ComponentsGeneratorUtils.js'); const { - convertDefaultTypeToString, generateStructName, + getDefaultInitializerString, getEnumMaskName, toIntEnumValueName, } = require('./CppHelpers.js'); @@ -456,13 +456,21 @@ function generateEnumString( function generatePropsString( componentName: string, props: $ReadOnlyArray>, + nameParts: $ReadOnlyArray, ) { return props .map(prop => { - const nativeType = getNativeTypeFromAnnotation(componentName, prop, []); - const defaultValue = convertDefaultTypeToString(componentName, prop); + const nativeType = getNativeTypeFromAnnotation( + componentName, + prop, + nameParts, + ); + const defaultInitializer = getDefaultInitializerString( + componentName, + prop, + ); - return `${nativeType} ${prop.name}{${defaultValue}};`; + return `${nativeType} ${prop.name}${defaultInitializer};`; }) .join('\n' + ' '); } @@ -629,16 +637,11 @@ function generateStruct( ): void { const structNameParts = nameParts; const structName = generateStructName(componentName, structNameParts); - - const fields = properties - .map(property => { - return `${getNativeTypeFromAnnotation( - componentName, - property, - structNameParts, - )} ${property.name};`; - }) - .join('\n' + ' '); + const fields = generatePropsString( + componentName, + properties, + structNameParts, + ); properties.forEach((property: NamedShape) => { const name = property.name; @@ -738,6 +741,7 @@ module.exports = { const propsString = generatePropsString( componentName, component.props, + [], ); const extendString = getClassExtendString(component); const extendsImports = getExtendsImports(component.extendsProps); diff --git a/packages/react-native-codegen/src/generators/components/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/components/__test_fixtures__/fixtures.js index 3b6915f33f9ec2..9121c8ad6878ee 100644 --- a/packages/react-native-codegen/src/generators/components/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/components/__test_fixtures__/fixtures.js @@ -871,6 +871,38 @@ const OBJECT_PROPS: SchemaType = { default: 0, }, }, + { + name: 'stringUserDefaultProp', + optional: true, + typeAnnotation: { + type: 'StringTypeAnnotation', + default: 'user_default', + }, + }, + { + name: 'booleanUserDefaultProp', + optional: true, + typeAnnotation: { + type: 'BooleanTypeAnnotation', + default: true, + }, + }, + { + name: 'floatUserDefaultProp', + optional: true, + typeAnnotation: { + type: 'FloatTypeAnnotation', + default: 3.14, + }, + }, + { + name: 'intUserDefaultProp', + optional: true, + typeAnnotation: { + type: 'Int32TypeAnnotation', + default: 9999, + }, + }, { name: 'stringEnumProp', optional: true, diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsH-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsH-test.js.snap index de244b9f33b380..fea966233c4ef2 100644 --- a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsH-test.js.snap +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsH-test.js.snap @@ -81,7 +81,7 @@ static inline std::string toString(const ArrayPropsNativeComponentSizesMask &val return result; } struct ArrayPropsNativeComponentObjectStruct { - std::string stringProp; + std::string stringProp{\\"\\"}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentObjectStruct &result) { @@ -108,7 +108,7 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu struct ArrayPropsNativeComponentArrayObjectStruct { - std::string stringProp; + std::string stringProp{\\"\\"}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentArrayObjectStruct &result) { @@ -135,7 +135,7 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu struct ArrayPropsNativeComponentArrayStruct { - std::vector object; + std::vector object{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentArrayStruct &result) { @@ -162,7 +162,7 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu struct ArrayPropsNativeComponentArrayOfArrayOfObjectStruct { - std::string stringProp; + std::string stringProp{\\"\\"}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentArrayOfArrayOfObjectStruct &result) { @@ -242,9 +242,9 @@ Map { namespace facebook::react { struct ArrayPropsNativeComponentNativePrimitivesStruct { - std::vector colors; - std::vector srcs; - std::vector points; + std::vector colors{}; + std::vector srcs{}; + std::vector points{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ArrayPropsNativeComponentNativePrimitivesStruct &result) { @@ -1100,7 +1100,7 @@ static inline std::string toString(const ObjectPropsIntEnumProp &value) { } } struct ObjectPropsObjectPropObjectArrayPropStruct { - std::vector array; + std::vector array{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropObjectArrayPropStruct &result) { @@ -1117,9 +1117,9 @@ static inline std::string toString(const ObjectPropsObjectPropObjectArrayPropStr } struct ObjectPropsObjectPropObjectPrimitiveRequiredPropStruct { - ImageSource image; - SharedColor color; - Point point; + ImageSource image{}; + SharedColor color{}; + Point point{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropObjectPrimitiveRequiredPropStruct &result) { @@ -1144,7 +1144,7 @@ static inline std::string toString(const ObjectPropsObjectPropObjectPrimitiveReq } struct ObjectPropsObjectPropNestedPropANestedPropBStruct { - std::string nestedPropC; + std::string nestedPropC{\\"\\"}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropNestedPropANestedPropBStruct &result) { @@ -1161,7 +1161,7 @@ static inline std::string toString(const ObjectPropsObjectPropNestedPropANestedP } struct ObjectPropsObjectPropNestedPropAStruct { - ObjectPropsObjectPropNestedPropANestedPropBStruct nestedPropB; + ObjectPropsObjectPropNestedPropANestedPropBStruct nestedPropB{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropNestedPropAStruct &result) { @@ -1178,7 +1178,7 @@ static inline std::string toString(const ObjectPropsObjectPropNestedPropAStruct } struct ObjectPropsObjectPropNestedArrayAsPropertyArrayPropStruct { - std::string stringProp; + std::string stringProp{\\"\\"}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropNestedArrayAsPropertyArrayPropStruct &result) { @@ -1205,7 +1205,7 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu struct ObjectPropsObjectPropNestedArrayAsPropertyStruct { - std::vector arrayProp; + std::vector arrayProp{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropNestedArrayAsPropertyStruct &result) { @@ -1222,16 +1222,20 @@ static inline std::string toString(const ObjectPropsObjectPropNestedArrayAsPrope } struct ObjectPropsObjectPropStruct { - std::string stringProp; - bool booleanProp; - Float floatProp; - int intProp; - ObjectPropsStringEnumProp stringEnumProp; - ObjectPropsIntEnumProp intEnumProp; - ObjectPropsObjectPropObjectArrayPropStruct objectArrayProp; - ObjectPropsObjectPropObjectPrimitiveRequiredPropStruct objectPrimitiveRequiredProp; - ObjectPropsObjectPropNestedPropAStruct nestedPropA; - ObjectPropsObjectPropNestedArrayAsPropertyStruct nestedArrayAsProperty; + std::string stringProp{\\"\\"}; + bool booleanProp{false}; + Float floatProp{0.0}; + int intProp{0}; + std::string stringUserDefaultProp{\\"user_default\\"}; + bool booleanUserDefaultProp{true}; + Float floatUserDefaultProp{3.14}; + int intUserDefaultProp{9999}; + ObjectPropsStringEnumProp stringEnumProp{ObjectPropsStringEnumProp::Option1}; + ObjectPropsIntEnumProp intEnumProp{ObjectPropsIntEnumProp::IntEnumProp0}; + ObjectPropsObjectPropObjectArrayPropStruct objectArrayProp{}; + ObjectPropsObjectPropObjectPrimitiveRequiredPropStruct objectPrimitiveRequiredProp{}; + ObjectPropsObjectPropNestedPropAStruct nestedPropA{}; + ObjectPropsObjectPropNestedArrayAsPropertyStruct nestedArrayAsProperty{}; }; static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ObjectPropsObjectPropStruct &result) { @@ -1253,6 +1257,22 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu if (tmp_intProp != map.end()) { fromRawValue(context, tmp_intProp->second, result.intProp); } + auto tmp_stringUserDefaultProp = map.find(\\"stringUserDefaultProp\\"); + if (tmp_stringUserDefaultProp != map.end()) { + fromRawValue(context, tmp_stringUserDefaultProp->second, result.stringUserDefaultProp); + } + auto tmp_booleanUserDefaultProp = map.find(\\"booleanUserDefaultProp\\"); + if (tmp_booleanUserDefaultProp != map.end()) { + fromRawValue(context, tmp_booleanUserDefaultProp->second, result.booleanUserDefaultProp); + } + auto tmp_floatUserDefaultProp = map.find(\\"floatUserDefaultProp\\"); + if (tmp_floatUserDefaultProp != map.end()) { + fromRawValue(context, tmp_floatUserDefaultProp->second, result.floatUserDefaultProp); + } + auto tmp_intUserDefaultProp = map.find(\\"intUserDefaultProp\\"); + if (tmp_intUserDefaultProp != map.end()) { + fromRawValue(context, tmp_intUserDefaultProp->second, result.intUserDefaultProp); + } auto tmp_stringEnumProp = map.find(\\"stringEnumProp\\"); if (tmp_stringEnumProp != map.end()) { fromRawValue(context, tmp_stringEnumProp->second, result.stringEnumProp); diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaPojo-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaPojo-test.js.snap index 47ce16d68f18f5..fe1eaec379e592 100644 --- a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaPojo-test.js.snap +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaPojo-test.js.snap @@ -1024,6 +1024,10 @@ public class ObjectPropsPropsObjectProp { private boolean mBooleanProp; private float mFloatProp; private int mIntProp; + private @Nullable String mStringUserDefaultProp; + private boolean mBooleanUserDefaultProp; + private float mFloatUserDefaultProp; + private int mIntUserDefaultProp; private @Nullable String mStringEnumProp; private @Nullable Integer mIntEnumProp; private ObjectPropsPropsObjectPropObjectArrayProp mObjectArrayProp; @@ -1047,6 +1051,22 @@ public class ObjectPropsPropsObjectProp { return mIntProp; } @DoNotStrip + public @Nullable String getStringUserDefaultProp() { + return mStringUserDefaultProp; + } + @DoNotStrip + public boolean getBooleanUserDefaultProp() { + return mBooleanUserDefaultProp; + } + @DoNotStrip + public float getFloatUserDefaultProp() { + return mFloatUserDefaultProp; + } + @DoNotStrip + public int getIntUserDefaultProp() { + return mIntUserDefaultProp; + } + @DoNotStrip public @Nullable String getStringEnumProp() { return mStringEnumProp; } diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index 2dc407f303476f..8335f705ae6eb6 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -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', ]); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap index a03ec04f54d55d..875476d45acaae 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap @@ -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 { diff --git a/packages/react-native-gradle-plugin/package.json b/packages/react-native-gradle-plugin/package.json index 33b450a9e9166c..b44a0a6e9e4843 100644 --- a/packages/react-native-gradle-plugin/package.json +++ b/packages/react-native-gradle-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/gradle-plugin", - "version": "0.74.0", + "version": "0.74.84", "description": "Gradle Plugin for React Native", "license": "MIT", "repository": { diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt index f3b55e091104a5..6b07391768308c 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt @@ -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)) } } diff --git a/packages/react-native-popup-menu-android/android/build.gradle.kts b/packages/react-native-popup-menu-android/android/build.gradle.kts new file mode 100644 index 00000000000000..d861b977491ea5 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/build.gradle.kts @@ -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")) +} diff --git a/packages/react-native-popup-menu-android/android/gradle.properties b/packages/react-native-popup-menu-android/android/gradle.properties new file mode 100644 index 00000000000000..f436a7474fed62 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/gradle.properties @@ -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 diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ComponentDescriptors.h b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ComponentDescriptors.h new file mode 100644 index 00000000000000..af069cd67fc885 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ComponentDescriptors.h @@ -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 + +namespace facebook::react { + +using AndroidPopupMenuComponentDescriptor = ConcreteComponentDescriptor; + +} // namespace facebook::react diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/EventEmitters.cpp b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/EventEmitters.cpp new file mode 100644 index 00000000000000..0c18d59e3a63db --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/EventEmitters.cpp @@ -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 diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/EventEmitters.h b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/EventEmitters.h new file mode 100644 index 00000000000000..a0387907710fe0 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/EventEmitters.h @@ -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 + + +namespace facebook::react { +class AndroidPopupMenuEventEmitter : public ViewEventEmitter { + public: + using ViewEventEmitter::ViewEventEmitter; + + struct OnSelectionChange { + int item; + }; + void onSelectionChange(OnSelectionChange value) const; +}; +} // namespace facebook::react diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/Props.cpp b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/Props.cpp new file mode 100644 index 00000000000000..ddcdcff88ebfef --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/Props.cpp @@ -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 +#include + +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 diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/Props.h b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/Props.h new file mode 100644 index 00000000000000..191f0b15776bc5 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/Props.h @@ -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 +#include +#include + +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 menuItems{}; +}; + +} // namespace facebook::react diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ShadowNodes.cpp b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ShadowNodes.cpp new file mode 100644 index 00000000000000..5fd96dbbadaacf --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ShadowNodes.cpp @@ -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 diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ShadowNodes.h b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ShadowNodes.h new file mode 100644 index 00000000000000..0fd9852b1bca57 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/ShadowNodes.h @@ -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 +#include + +namespace facebook::react { + +JSI_EXPORT extern const char AndroidPopupMenuComponentName[]; + +/* + * `ShadowNode` for component. + */ +using AndroidPopupMenuShadowNode = ConcreteViewShadowNode< + AndroidPopupMenuComponentName, + AndroidPopupMenuProps, + AndroidPopupMenuEventEmitter, + AndroidPopupMenuState>; + +} // namespace facebook::react diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/States.cpp b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/States.cpp new file mode 100644 index 00000000000000..1dbb184cbddb62 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/States.cpp @@ -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 diff --git a/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/States.h b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/States.h new file mode 100644 index 00000000000000..62c048033b7021 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/jni/react/renderer/components/ReactPopupMenuAndroidSpecs/States.h @@ -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 +#include +#include +#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 diff --git a/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/PopupMenuPackage.kt b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/PopupMenuPackage.kt new file mode 100644 index 00000000000000..0a351d28643ee0 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/PopupMenuPackage.kt @@ -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? = null + + override fun getModule(name: String, context: ReactApplicationContext): NativeModule? { + return null + } + + private fun getViewManagersMap(): Map { + val viewManagers = + viewManagersMap + ?: mapOf( + ReactPopupMenuManager.REACT_CLASS to + ModuleSpec.viewManagerSpec({ ReactPopupMenuManager() })) + viewManagersMap = viewManagers + return viewManagers + } + + protected override fun getViewManagers(context: ReactApplicationContext): List { + return ArrayList(getViewManagersMap().values) + } + + override fun getViewManagerNames(context: ReactApplicationContext): Collection { + 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() } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/PopupMenuSelectionEvent.kt b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/PopupMenuSelectionEvent.kt similarity index 89% rename from packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/PopupMenuSelectionEvent.kt rename to packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/PopupMenuSelectionEvent.kt index f0195e6d29ff4d..289b70d566c870 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/PopupMenuSelectionEvent.kt +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/PopupMenuSelectionEvent.kt @@ -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 diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/ReactPopupMenuContainer.kt b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/ReactPopupMenuContainer.kt similarity index 97% rename from packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/ReactPopupMenuContainer.kt rename to packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/ReactPopupMenuContainer.kt index 57574290bacbe3..6be3bfc916d059 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/ReactPopupMenuContainer.kt +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/ReactPopupMenuContainer.kt @@ -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 diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/ReactPopupMenuManager.kt b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/ReactPopupMenuManager.kt similarity index 97% rename from packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/ReactPopupMenuManager.kt rename to packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/ReactPopupMenuManager.kt index 99dd6b72aa3c92..3758f154c8741e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/popupmenu/ReactPopupMenuManager.kt +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/popupmenu/ReactPopupMenuManager.kt @@ -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 diff --git a/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java new file mode 100644 index 00000000000000..736b778e5948f5 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java @@ -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 & AndroidPopupMenuManagerInterface> extends BaseViewManagerDelegate { + 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; + } + } +} diff --git a/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerInterface.java b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerInterface.java new file mode 100644 index 00000000000000..83e94ab5e295a2 --- /dev/null +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerInterface.java @@ -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 { + void setMenuItems(T view, @Nullable ReadableArray value); + void show(T view); +} diff --git a/packages/react-native-popup-menu-android/index.d.ts b/packages/react-native-popup-menu-android/index.d.ts new file mode 100644 index 00000000000000..9539572f172798 --- /dev/null +++ b/packages/react-native-popup-menu-android/index.d.ts @@ -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'; diff --git a/packages/react-native-popup-menu-android/index.js b/packages/react-native-popup-menu-android/index.js new file mode 100644 index 00000000000000..f433cc1331389f --- /dev/null +++ b/packages/react-native-popup-menu-android/index.js @@ -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'; diff --git a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.android.js b/packages/react-native-popup-menu-android/js/PopupMenuAndroid.android.js similarity index 89% rename from packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.android.js rename to packages/react-native-popup-menu-android/js/PopupMenuAndroid.android.js index 035a1da5b150d6..b97d45cb5a476d 100644 --- a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.android.js +++ b/packages/react-native-popup-menu-android/js/PopupMenuAndroid.android.js @@ -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'; diff --git a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.d.ts b/packages/react-native-popup-menu-android/js/PopupMenuAndroid.d.ts similarity index 89% rename from packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.d.ts rename to packages/react-native-popup-menu-android/js/PopupMenuAndroid.d.ts index 435df11d487fe7..8eab4875f38347 100644 --- a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.d.ts +++ b/packages/react-native-popup-menu-android/js/PopupMenuAndroid.d.ts @@ -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; diff --git a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.js b/packages/react-native-popup-menu-android/js/PopupMenuAndroid.js similarity index 50% rename from packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.js rename to packages/react-native-popup-menu-android/js/PopupMenuAndroid.js index 94677cd6b8d23d..1331d32bd29722 100644 --- a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid.js +++ b/packages/react-native-popup-menu-android/js/PopupMenuAndroid.js @@ -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 ( + {this.props.children} + ); + } +} + +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 ; + return {props.children}; } export default PopupMenuAndroid; diff --git a/packages/react-native/src/private/specs/components/PopupMenuAndroidNativeComponent.js b/packages/react-native-popup-menu-android/js/PopupMenuAndroidNativeComponent.android.js similarity index 68% rename from packages/react-native/src/private/specs/components/PopupMenuAndroidNativeComponent.js rename to packages/react-native-popup-menu-android/js/PopupMenuAndroidNativeComponent.android.js index 81801f65f4d0dd..7558d75415bfb0 100644 --- a/packages/react-native/src/private/specs/components/PopupMenuAndroidNativeComponent.js +++ b/packages/react-native-popup-menu-android/js/PopupMenuAndroidNativeComponent.android.js @@ -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, diff --git a/packages/react-native-popup-menu-android/package.json b/packages/react-native-popup-menu-android/package.json new file mode 100644 index 00000000000000..d8aa2eda1ac062 --- /dev/null +++ b/packages/react-native-popup-menu-android/package.json @@ -0,0 +1,47 @@ +{ + "name": "@react-native/popup-menu-android", + "version": "0.74.84", + "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.84" + }, + "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" + } + } +} diff --git a/packages/react-native-test-renderer/package.json b/packages/react-native-test-renderer/package.json index 4b90093f6fc2cb..f3e232fbaebee3 100644 --- a/packages/react-native-test-renderer/package.json +++ b/packages/react-native-test-renderer/package.json @@ -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.84", + "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" } +} diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate+Protected.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate+Protected.h new file mode 100644 index 00000000000000..2321b43c4da971 --- /dev/null +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate+Protected.h @@ -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. + */ + +#if defined(__cplusplus) + +#import +#import "RCTAppDelegate.h" + +@interface RCTAppDelegate () +@end + +#endif diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h index dd234fb7b1616d..d61c3d115e3044 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h @@ -7,6 +7,7 @@ #import #import +#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 diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index a53a42303d7d40..18b299b539193c 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -11,7 +11,11 @@ #import #import #import -#import +#import +#import +#import +#import +#import "RCTAppDelegate+Protected.h" #import "RCTAppSetupUtils.h" #if RN_DISABLE_OSS_PLUGIN_HEADER @@ -39,84 +43,29 @@ #import #import -@interface RCTAppDelegate () < - RCTTurboModuleManagerDelegate, - RCTComponentViewFactoryComponentProvider, - RCTContextContainerHandling> { - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end - -static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled) -{ - NSMutableDictionary *mutableProps = [initialProps mutableCopy] ?: [NSMutableDictionary new]; - return mutableProps; -} - -@interface RCTAppDelegate () { - std::shared_ptr _runtimeScheduler; -} +@interface RCTAppDelegate () @end -@implementation RCTAppDelegate { - RCTHost *_reactHost; -} - -- (instancetype)init -{ - if (self = [super init]) { - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _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; - - NSDictionary *initProps = updateInitialProps([self prepareInitialProps], fabricEnabled); + [self _setUpFeatureFlags]; - RCTAppSetupPrepareApp(application, enableTM); + RCTSetNewArchEnabled([self newArchEnabled]); + RCTAppSetupPrepareApp(application, self.turboModuleEnabled); - UIView *rootView; - if (enableBridgeless) { - // Enable native view config interop only if both bridgeless mode and Fabric is enabled. - RCTSetUseNativeViewConfigsInBridgelessMode(fabricEnabled); + self.rootViewFactory = [self createRCTRootViewFactory]; - // Enable TurboModule interop by default in Bridgeless mode - RCTEnableTurboModuleInterop(YES); - RCTEnableTurboModuleInteropBridgeProxy(YES); + UIView *rootView = [self.rootViewFactory viewWithModuleName:self.moduleName + initialProperties:self.initialProps + launchOptions:launchOptions]; - [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 customizeRootView:(RCTRootView *)rootView]; + [self _logWarnIfCreateRootViewWithBridgeIsOverridden]; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [self createRootViewController]; [self setRootView:rootView toRootViewController:rootViewController]; @@ -139,26 +88,15 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge return nil; } -- (NSDictionary *)prepareInitialProps -{ - return self.initialProps; -} - - (RCTBridge *)createBridgeWithDelegate:(id)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 +130,9 @@ - (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *) rootViewController.view = rootView; } -- (BOOL)runtimeSchedulerEnabled +- (void)customizeRootView:(RCTRootView *)rootView { - return YES; + // Override point for customization after application launch. } #pragma mark - UISceneDelegate @@ -207,25 +145,6 @@ - (void)windowScene:(UIWindowScene *)windowScene [[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self]; } -#pragma mark - RCTCxxBridgeDelegate - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - _runtimeScheduler = std::make_shared(RCTRuntimeExecutorFromBridge(bridge)); - if ([self newArchEnabled]) { - std::shared_ptr callInvoker = - std::make_shared(_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 +171,33 @@ - (BOOL)bridgelessEnabled return [self newArchEnabled]; } -#pragma mark - RCTComponentViewFactoryComponentProvider +- (NSURL *)bundleURL +{ + [NSException raise:@"RCTAppDelegate::bundleURL not implemented" + format:@"Subclasses must implement a valid getBundleURL method"]; + return nullptr; +} -- (NSDictionary> *)thirdPartyFabricComponents +#pragma mark - Bridge and Bridge Adapter properties + +- (RCTBridge *)bridge { - return @{}; + 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 +229,67 @@ - (Class)getModuleClassFromName:(const char *)name return RCTAppSetupDefaultModuleFromClass(moduleClass); } -#pragma mark - New Arch Utilities +#pragma mark - RCTComponentViewFactoryComponentProvider -- (void)createReactHost +- (NSDictionary> *)thirdPartyFabricComponents { - __weak __typeof(self) weakSelf = self; - _reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL] - hostDelegate:nil - turboModuleManagerDelegate:self - jsEngineProvider:^std::shared_ptr() { - return [weakSelf createJSRuntimeFactory]; - }]; - [_reactHost setBundleURLProvider:^NSURL *() { - return [weakSelf bundleURL]; - }]; - [_reactHost setContextContainerHandler:self]; - [_reactHost start]; + return @{}; } -- (std::shared_ptr)createJSRuntimeFactory +- (RCTRootViewFactory *)createRCTRootViewFactory { -#if USE_HERMES - return std::make_shared(_reactNativeConfig, nullptr); -#else - return std::make_shared(); -#endif + __weak __typeof(self) weakSelf = self; + RCTBundleURLBlock bundleUrlBlock = ^{ + RCTAppDelegate *strongSelf = weakSelf; + return strongSelf.bundleURL; + }; + + RCTRootViewFactoryConfiguration *configuration = + [[RCTRootViewFactoryConfiguration alloc] initWithBundleURLBlock:bundleUrlBlock + newArchEnabled:self.fabricEnabled + turboModuleEnabled:self.turboModuleEnabled + bridgelessEnabled:self.bridgelessEnabled]; + + configuration.createRootViewWithBridge = ^UIView *(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps) + { + return [weakSelf createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps]; + }; + + configuration.createBridgeWithDelegate = ^RCTBridge *(id delegate, NSDictionary *launchOptions) + { + return [weakSelf createBridgeWithDelegate:delegate launchOptions:launchOptions]; + }; + + configuration.customizeRootView = ^(UIView *_Nonnull rootView) { + [weakSelf customizeRootView:(RCTRootView *)rootView]; + }; + + return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self]; } -- (void)didCreateContextContainer:(std::shared_ptr)contextContainer -{ - contextContainer->insert("ReactNativeConfig", _reactNativeConfig); -} +#pragma mark - Feature Flags -- (NSURL *)bundleURL +class RCTAppDelegateBridgelessFeatureFlags : public facebook::react::ReactNativeFeatureFlagsDefaults { + public: + bool useModernRuntimeScheduler() override + { + return true; + } + bool enableMicrotasks() override + { + return true; + } + bool batchRenderingUpdatesInEventLoop() override + { + return true; + } +}; + +- (void)_setUpFeatureFlags { - [NSException raise:@"RCTAppDelegate::bundleURL not implemented" - format:@"Subclasses must implement a valid getBundleURL method"]; - return nullptr; + if ([self bridgelessEnabled]) { + facebook::react::ReactNativeFeatureFlags::override(std::make_unique()); + } } @end diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h new file mode 100644 index 00000000000000..d5cd14cce15f0d --- /dev/null +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h @@ -0,0 +1,141 @@ +/* + * 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 +#import +#import + +@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 delegate, NSDictionary *launchOptions); +typedef void (^RCTCustomizeRootViewBlock)(UIView *rootView); +typedef NSURL *_Nullable (^RCTSourceURLForBridgeBlock)(RCTBridge *bridge); +typedef NSURL *_Nullable (^RCTBundleURLBlock)(void); +typedef NSArray> *_Nonnull (^RCTExtraModulesForBridgeBlock)(RCTBridge *bridge); +typedef NSDictionary *_Nonnull (^RCTExtraLazyModuleClassesForBridge)(RCTBridge *bridge); +typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *moduleName); + +#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, nonnull) RCTBundleURLBlock bundleURLBlock; + +/** + * 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)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock + newArchEnabled:(BOOL)newArchEnabled + turboModuleEnabled:(BOOL)turboModuleEnabled + bridgelessEnabled:(BOOL)bridgelessEnabled NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + newArchEnabled:(BOOL)newArchEnabled + turboModuleEnabled:(BOOL)turboModuleEnabled + bridgelessEnabled:(BOOL)bridgelessEnabled __deprecated; + +/** + * 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; + +/** + * Block that allows to customize the rootView that is passed to React Native. + * + * @parameter: rootView - The root view to customize. + */ +@property (nonatomic, nullable) RCTCustomizeRootViewBlock customizeRootView; + +@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)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 diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm new file mode 100644 index 00000000000000..7131d3f876cac0 --- /dev/null +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -0,0 +1,276 @@ +/* + * 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 +#import +#import +#import +#import +#import +#import "RCTAppDelegate.h" +#import "RCTAppSetupUtils.h" + +#if RN_DISABLE_OSS_PLUGIN_HEADER +#import +#else +#import +#endif +#import +#import +#import +#import +#import +#import +#import +#if USE_HERMES +#import +#else +#import +#endif +#import +#import +#import +#import +#import +#import +#import + +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 +{ + return [self + initWithBundleURLBlock:^{ + return bundleURL; + } + newArchEnabled:newArchEnabled + turboModuleEnabled:turboModuleEnabled + bridgelessEnabled:bridgelessEnabled]; +} + +- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock + newArchEnabled:(BOOL)newArchEnabled + turboModuleEnabled:(BOOL)turboModuleEnabled + bridgelessEnabled:(BOOL)bridgelessEnabled +{ + if (self = [super init]) { + _bundleURLBlock = bundleURLBlock; + _fabricEnabled = newArchEnabled; + _turboModuleEnabled = turboModuleEnabled; + _bridgelessEnabled = bridgelessEnabled; + } + return self; +} + +@end + +@interface RCTRootViewFactory () { + std::shared_ptr _reactNativeConfig; + facebook::react::ContextContainer::Shared _contextContainer; +} +@end + +@interface RCTRootViewFactory () { + std::shared_ptr _runtimeScheduler; +} +@end + +@implementation RCTRootViewFactory { + RCTHost *_reactHost; + RCTRootViewFactoryConfiguration *_configuration; + __weak id _turboModuleManagerDelegate; +} + +- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration + andTurboModuleManagerDelegate:(id)turboModuleManagerDelegate +{ + if (self = [super init]) { + _configuration = configuration; + _contextContainer = std::make_shared(); + _reactNativeConfig = std::make_shared(); + _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:launchOptions]; + + RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps]; + + RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc] + initWithSurface:surface + sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact]; + + if (self->_configuration.customizeRootView != nil) { + self->_configuration.customizeRootView(surfaceHostingProxyRootView); + } + return surfaceHostingProxyRootView; + } + + [self createBridgeIfNeeded:launchOptions]; + [self createBridgeAdapterIfNeeded]; + + UIView *rootView; + if (self->_configuration.createRootViewWithBridge != nil) { + rootView = self->_configuration.createRootViewWithBridge(self.bridge, moduleName, initProps); + } else { + rootView = [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps]; + } + if (self->_configuration.customizeRootView != nil) { + self->_configuration.customizeRootView(rootView); + } + return rootView; +} + +- (RCTBridge *)createBridgeWithDelegate:(id)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)jsExecutorFactoryForBridge:(RCTBridge *)bridge +{ + _runtimeScheduler = std::make_shared(RCTRuntimeExecutorFromBridge(bridge)); + if (RCTIsNewArchEnabled()) { + std::shared_ptr callInvoker = + std::make_shared(_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:(NSDictionary *)launchOptions +{ + if (_reactHost) { + return; + } + + __weak __typeof(self) weakSelf = self; + _reactHost = [[RCTHost alloc] initWithBundleURLProvider:self->_configuration.bundleURLBlock + hostDelegate:nil + turboModuleManagerDelegate:_turboModuleManagerDelegate + jsEngineProvider:^std::shared_ptr() { + return [weakSelf createJSRuntimeFactory]; + } + launchOptions:launchOptions]; + [_reactHost setBundleURLProvider:^NSURL *() { + return [weakSelf bundleURL]; + }]; + [_reactHost setContextContainerHandler:self]; + [_reactHost start]; +} + +- (std::shared_ptr)createJSRuntimeFactory +{ +#if USE_HERMES + return std::make_shared(_reactNativeConfig, nullptr); +#else + return std::make_shared(); +#endif +} + +- (void)didCreateContextContainer:(std::shared_ptr)contextContainer +{ + contextContainer->insert("ReactNativeConfig", _reactNativeConfig); +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ + return [self bundleURL]; +} + +- (NSURL *)bundleURL +{ + return self->_configuration.bundleURLBlock(); +} + +@end diff --git a/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec b/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec index 146ff1ea5ccf83..4d5bf68dc4c80f 100644 --- a/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec +++ b/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec @@ -89,6 +89,7 @@ Pod::Spec.new do |s| add_dependency(s, "React-utils") add_dependency(s, "React-debug") add_dependency(s, "React-rendererdebug") + add_dependency(s, "React-featureflags") if use_hermes s.dependency "React-hermes" diff --git a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroidNativeComponent.js b/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroidNativeComponent.js deleted file mode 100644 index 05bbd5693155c5..00000000000000 --- a/packages/react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroidNativeComponent.js +++ /dev/null @@ -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; diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index 7b522fe5dcb026..a377366dc7079d 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -1135,12 +1135,14 @@ function InternalTextInput(props: Props): React.Node { }; const [mostRecentEventCount, setMostRecentEventCount] = useState(0); - const [lastNativeText, setLastNativeText] = useState(props.value); const [lastNativeSelectionState, setLastNativeSelection] = useState<{| - selection: ?Selection, + selection: Selection, mostRecentEventCount: number, - |}>({selection, mostRecentEventCount}); + |}>({ + selection: {start: -1, end: -1}, + mostRecentEventCount: mostRecentEventCount, + }); const lastNativeSelection = lastNativeSelectionState.selection; @@ -1498,6 +1500,7 @@ function InternalTextInput(props: Props): React.Node { onSelectionChange={_onSelectionChange} onSelectionChangeShouldSetResponder={emptyFunctionThatReturnsTrue} selection={selection} + selectionColor={selectionColor} style={StyleSheet.compose( useMultilineDefaultStyle ? styles.multilineDefault : null, style, diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js b/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js index 0a0103aa6c6e02..f0223835384dd3 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js @@ -209,6 +209,7 @@ class TouchableBounce extends React.Component { componentWillUnmount(): void { this.state.pressability.reset(); + this.state.scale.resetAnimation(); } } diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js b/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js index 8e6db94153970b..a11eb7cafe3fb9 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js @@ -320,6 +320,7 @@ class TouchableOpacity extends React.Component { componentWillUnmount(): void { this.state.pressability.reset(); + this.state.anim.resetAnimation(); } } diff --git a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js index e870c99355d1e0..e0c82a319f3756 100644 --- a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -144,6 +144,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { borderTopLeftRadius: true, borderTopRightRadius: true, borderTopStartRadius: true, + cursor: true, opacity: true, pointerEvents: true, diff --git a/packages/react-native/Libraries/Core/ReactNativeVersion.js b/packages/react-native/Libraries/Core/ReactNativeVersion.js index 2f45659dc6c94a..c3bd7491bd5eea 100644 --- a/packages/react-native/Libraries/Core/ReactNativeVersion.js +++ b/packages/react-native/Libraries/Core/ReactNativeVersion.js @@ -14,9 +14,9 @@ const version: $ReadOnly<{ patch: number, prerelease: string | null, }> = { - major: 1000, - minor: 0, - patch: 0, + major: 0, + minor: 74, + patch: 2, prerelease: null, }; diff --git a/packages/react-native/Libraries/Core/registerCallableModule.d.ts b/packages/react-native/Libraries/Core/registerCallableModule.d.ts new file mode 100644 index 00000000000000..05d9e92da7c717 --- /dev/null +++ b/packages/react-native/Libraries/Core/registerCallableModule.d.ts @@ -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; diff --git a/packages/react-native/Libraries/Lists/FlatList.js b/packages/react-native/Libraries/Lists/FlatList.js index 3d267cb95988ed..f34fe50f8721ee 100644 --- a/packages/react-native/Libraries/Lists/FlatList.js +++ b/packages/react-native/Libraries/Lists/FlatList.js @@ -308,7 +308,6 @@ export type Props = { * Also inherits [ScrollView Props](docs/scrollview.html#props), unless it is nested in another FlatList of same orientation. */ class FlatList extends React.PureComponent, void> { - props: Props; /** * Scrolls to the end of the content. May be janky without `getItemLayout` prop. */ diff --git a/packages/react-native/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js b/packages/react-native/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js index 42a2a9e6950daa..20ab886c9554bf 100644 --- a/packages/react-native/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js +++ b/packages/react-native/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js @@ -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([ diff --git a/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js b/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js index 744fa5e4e367ef..940971d76e45c9 100644 --- a/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js +++ b/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js @@ -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; } diff --git a/packages/react-native/Libraries/ReactNative/AppContainer-dev.js b/packages/react-native/Libraries/ReactNative/AppContainer-dev.js index 05c97b5d44de1a..cba0ccb2395dce 100644 --- a/packages/react-native/Libraries/ReactNative/AppContainer-dev.js +++ b/packages/react-native/Libraries/ReactNative/AppContainer-dev.js @@ -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 ( + + ); +}; + const AppContainer = ({ children, fabric, @@ -155,7 +174,7 @@ const AppContainer = ({ {reactDevToolsAgent != null && ( - diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts index 5a9121be11765f..f4a99256313845 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts @@ -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; } diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js index d4b5ab921132fc..a2d7a3310ef343 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js @@ -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<{ diff --git a/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm b/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm index 582b49c1ef4f4b..3fb6c8d622611e 100644 --- a/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm +++ b/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm @@ -163,6 +163,12 @@ - (void)setSelectedTextRange:(UITextRange *)selectedTextRange notifyDelegate:(BO [super setSelectedTextRange:selectedTextRange]; } +// After restoring the previous cursor position, we manually trigger the scroll to the new cursor position (PR 38679). +- (void)scrollRangeToVisible:(NSRange)range +{ + [super scrollRangeToVisible:range]; +} + - (void)paste:(id)sender { _textWasPasted = YES; diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h index a8719ecd4d0165..7aa5bcd54e7b36 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -43,6 +43,7 @@ NS_ASSUME_NONNULL_BEGIN // If the change was a result of user actions (like typing or touches), we MUST notify the delegate. - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE; - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate; +- (void)scrollRangeToVisible:(NSRange)selectedTextRange; // This protocol disallows direct access to `text` property because // unwise usage of it can break the `attributeText` behavior. diff --git a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm index 4d0afd97ae682a..82b186ead5f3dc 100644 --- a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm +++ b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm @@ -201,6 +201,11 @@ - (void)setSelectedTextRange:(UITextRange *)selectedTextRange notifyDelegate:(BO [super setSelectedTextRange:selectedTextRange]; } +- (void)scrollRangeToVisible:(NSRange)range +{ + // Singleline TextInput does not require scrolling after calling setSelectedTextRange (PR 38679). +} + - (void)paste:(id)sender { _textWasPasted = YES; diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index de27ba219ed603..505a80ea240c77 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -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, - onSelectionChange: (number) => void, - children: React.Node, - instanceRef: RefObject, -}; -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, - onSelectionChange: (number) => void, - children: Node, - instanceRef: RefObject, -}; -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, \\"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, diff --git a/packages/react-native/React/Base/RCTBridge+Inspector.h b/packages/react-native/React/Base/RCTBridge+Inspector.h new file mode 100644 index 00000000000000..25c3732a3c52be --- /dev/null +++ b/packages/react-native/React/Base/RCTBridge+Inspector.h @@ -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 + +#ifdef __cplusplus +#import +#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 diff --git a/packages/react-native/React/Base/RCTBridge+Private.h b/packages/react-native/React/Base/RCTBridge+Private.h index d9b2be4bab269c..4d03df3ce8d607 100644 --- a/packages/react-native/React/Base/RCTBridge+Private.h +++ b/packages/react-native/React/Base/RCTBridge+Private.h @@ -6,9 +6,6 @@ */ #import -#ifdef __cplusplus -#import -#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. diff --git a/packages/react-native/React/Base/RCTBridge.mm b/packages/react-native/React/Base/RCTBridge.mm index 65d827e163be5f..9c1890542e869a 100644 --- a/packages/react-native/React/Base/RCTBridge.mm +++ b/packages/react-native/React/Base/RCTBridge.mm @@ -6,6 +6,7 @@ */ #import "RCTBridge.h" +#import "RCTBridge+Inspector.h" #import "RCTBridge+Private.h" #import diff --git a/packages/react-native/React/Base/RCTBridgeProxy+Cxx.h b/packages/react-native/React/Base/RCTBridgeProxy+Cxx.h new file mode 100644 index 00000000000000..2748178a3dbb6e --- /dev/null +++ b/packages/react-native/React/Base/RCTBridgeProxy+Cxx.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 +#endif + +#import "RCTBridgeProxy.h" + +@interface RCTBridgeProxy (Cxx) + +#ifdef __cplusplus +@property (nonatomic, readwrite) std::shared_ptr jsCallInvoker; +#endif + +@end diff --git a/packages/react-native/React/Base/RCTBridgeProxy.h b/packages/react-native/React/Base/RCTBridgeProxy.h index 79c75c3b491ae0..71bb9cf0831640 100644 --- a/packages/react-native/React/Base/RCTBridgeProxy.h +++ b/packages/react-native/React/Base/RCTBridgeProxy.h @@ -9,19 +9,23 @@ #import "RCTBridgeModule.h" +NS_ASSUME_NONNULL_BEGIN + @class RCTBundleManager; @class RCTCallableJSModules; @class RCTModuleRegistry; @class RCTViewRegistry; @interface RCTBridgeProxy : NSProxy + - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry moduleRegistry:(RCTModuleRegistry *)moduleRegistry bundleManager:(RCTBundleManager *)bundleManager callableJSModules:(RCTCallableJSModules *)callableJSModules dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId - runtime:(void *)runtime NS_DESIGNATED_INITIALIZER; + runtime:(void *)runtime + launchOptions:(nullable NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; - (void)forwardInvocation:(NSInvocation *)invocation; @@ -34,4 +38,7 @@ */ - (id)moduleForClass:(Class)moduleClass; - (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad; + @end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Base/RCTBridgeProxy.mm b/packages/react-native/React/Base/RCTBridgeProxy.mm index 6c1e914c2f86b7..5b3e0656929ee3 100644 --- a/packages/react-native/React/Base/RCTBridgeProxy.mm +++ b/packages/react-native/React/Base/RCTBridgeProxy.mm @@ -6,10 +6,13 @@ */ #import "RCTBridgeProxy.h" +#import "RCTBridgeProxy+Cxx.h" + #import #import #import #import +#import #import using namespace facebook; @@ -21,11 +24,18 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; - (void)forwardInvocation:(NSInvocation *)invocation; @end +@interface RCTBridgeProxy () + +@property (nonatomic, readwrite) std::shared_ptr jsCallInvoker; + +@end + @implementation RCTBridgeProxy { RCTUIManagerProxy *_uiManagerProxy; RCTModuleRegistry *_moduleRegistry; RCTBundleManager *_bundleManager; RCTCallableJSModules *_callableJSModules; + NSDictionary *_launchOptions; void (^_dispatchToJSThread)(dispatch_block_t); void (^_registerSegmentWithId)(NSNumber *, NSString *); void *_runtime; @@ -38,6 +48,7 @@ - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId runtime:(void *)runtime + launchOptions:(nullable NSDictionary *)launchOptions { self = [super self]; if (self) { @@ -48,6 +59,7 @@ - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry _dispatchToJSThread = dispatchToJSThread; _registerSegmentWithId = registerSegmentWithId; _runtime = runtime; + _launchOptions = [launchOptions copy]; } return self; } @@ -84,6 +96,12 @@ - (void *)runtime return _runtime; } +- (std::shared_ptr)jsCallInvoker +{ + [self logWarning:@"Please migrate to RuntimeExecutor" cmd:_cmd]; + return _jsCallInvoker; +} + /** * RCTModuleRegistry */ @@ -176,8 +194,7 @@ - (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path - (NSDictionary *)launchOptions { - [self logError:@"This method is not supported. Returning nil." cmd:_cmd]; - return nil; + return _launchOptions; } - (BOOL)loading diff --git a/packages/react-native/React/Base/RCTConvert.h b/packages/react-native/React/Base/RCTConvert.h index 2d55e7c99acf12..c853e9fb0c7bf3 100644 --- a/packages/react-native/React/Base/RCTConvert.h +++ b/packages/react-native/React/Base/RCTConvert.h @@ -11,6 +11,7 @@ #import #import #import +#import #import #import #import @@ -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; diff --git a/packages/react-native/React/Base/RCTConvert.mm b/packages/react-native/React/Base/RCTConvert.mm index 1367d93a873291..b868ee72f65ce8 100644 --- a/packages/react-native/React/Base/RCTConvert.mm +++ b/packages/react-native/React/Base/RCTConvert.mm @@ -545,6 +545,15 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC 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; diff --git a/packages/react-native/React/Base/RCTUtils.m b/packages/react-native/React/Base/RCTUtils.m index 96e59e2db10181..226a2bc8604481 100644 --- a/packages/react-native/React/Base/RCTUtils.m +++ b/packages/react-native/React/Base/RCTUtils.m @@ -562,17 +562,37 @@ BOOL RCTRunningInAppExtension(void) return nil; } - for (UIScene *scene in RCTSharedApplication().connectedScenes) { - if (scene.activationState != UISceneActivationStateForegroundActive || - ![scene isKindOfClass:[UIWindowScene class]]) { + NSSet *connectedScenes = RCTSharedApplication().connectedScenes; + + UIScene *foregroundActiveScene; + UIScene *foregroundInactiveScene; + + for (UIScene *scene in connectedScenes) { + if (![scene isKindOfClass:[UIWindowScene class]]) { continue; } - UIWindowScene *windowScene = (UIWindowScene *)scene; - for (UIWindow *window in windowScene.windows) { - if (window.isKeyWindow) { - return window; - } + if (scene.activationState == UISceneActivationStateForegroundActive) { + foregroundActiveScene = scene; + break; + } + + if (!foregroundInactiveScene && scene.activationState == UISceneActivationStateForegroundInactive) { + foregroundInactiveScene = scene; + // no break, we can have the active scene later in the set. + } + } + + UIScene *sceneToUse = foregroundActiveScene ? foregroundActiveScene : foregroundInactiveScene; + UIWindowScene *windowScene = (UIWindowScene *)sceneToUse; + + if (@available(iOS 15.0, *)) { + return windowScene.keyWindow; + } + + for (UIWindow *window in windowScene.windows) { + if (window.isKeyWindow) { + return window; } } diff --git a/packages/react-native/React/Base/RCTVersion.m b/packages/react-native/React/Base/RCTVersion.m index 26d701dd303104..568023ba844f58 100644 --- a/packages/react-native/React/Base/RCTVersion.m +++ b/packages/react-native/React/Base/RCTVersion.m @@ -21,9 +21,9 @@ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^(void){ __rnVersion = @{ - RCTVersionMajor: @(1000), - RCTVersionMinor: @(0), - RCTVersionPatch: @(0), + RCTVersionMajor: @(0), + RCTVersionMinor: @(74), + RCTVersionPatch: @(2), RCTVersionPrerelease: [NSNull null], }; }); diff --git a/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm b/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm index 670c02299601ec..39377fe8b6f846 100644 --- a/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm +++ b/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm @@ -125,9 +125,6 @@ - (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage [super surface:surface didChangeStage:stage]; if (RCTSurfaceStageIsRunning(stage)) { [_bridge.performanceLogger markStopForTag:RCTPLTTI]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:RCTContentDidAppearNotification object:self]; - }); } } diff --git a/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h b/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h index 47096221a7df62..3de4c5898c1558 100644 --- a/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h +++ b/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h @@ -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 diff --git a/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm b/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm index 78f991eecfb091..cb4ebeba3f97f4 100644 --- a/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm +++ b/packages/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm @@ -24,6 +24,7 @@ @implementation RCTSurfaceHostingView { UIView *_Nullable _activityIndicatorView; UIView *_Nullable _surfaceView; RCTSurfaceStage _stage; + BOOL _autoHideDisabled; } RCT_NOT_IMPLEMENTED(-(instancetype)init) @@ -36,6 +37,7 @@ - (instancetype)initWithSurface:(id)surface if (self = [super initWithFrame:CGRectZero]) { _surface = surface; _sizeMeasureMode = sizeMeasureMode; + _autoHideDisabled = NO; _surface.delegate = self; _stage = surface.stage; @@ -124,6 +126,10 @@ - (void)setSizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode _sizeMeasureMode = sizeMeasureMode; [self _invalidateLayout]; } +- (void)disableActivityIndicatorAutoHide:(BOOL)disabled +{ + _autoHideDisabled = disabled; +} #pragma mark - isActivityIndicatorViewVisible @@ -162,7 +168,16 @@ - (void)setIsSurfaceViewVisible:(BOOL)visible _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 @@ - (void)_invalidateLayout - (void)_updateViews { self.isSurfaceViewVisible = RCTSurfaceStageIsRunning(_stage); - self.isActivityIndicatorViewVisible = RCTSurfaceStageIsPreparing(_stage); + self.isActivityIndicatorViewVisible = _autoHideDisabled || RCTSurfaceStageIsPreparing(_stage); } - (void)didMoveToWindow diff --git a/packages/react-native/React/CoreModules/RCTDevMenu.mm b/packages/react-native/React/CoreModules/RCTDevMenu.mm index 9e220790d55fa4..2f57d4b0b46a3d 100644 --- a/packages/react-native/React/CoreModules/RCTDevMenu.mm +++ b/packages/react-native/React/CoreModules/RCTDevMenu.mm @@ -396,7 +396,7 @@ - (void)setDefaultJSBundle ? UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert; - NSString *devMenuType = self.bridge ? @"Bridge" : @"Bridgeless"; + NSString *devMenuType = [self.bridge isKindOfClass:RCTBridge.class] ? @"Bridge" : @"Bridgeless"; NSString *devMenuTitle = [NSString stringWithFormat:@"React Native Dev Menu (%@)", devMenuType]; _actionSheet = [UIAlertController alertControllerWithTitle:devMenuTitle message:description preferredStyle:style]; diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.mm b/packages/react-native/React/CoreModules/RCTDevSettings.mm index b5e81c940e39f6..887a69c06c1b37 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.mm +++ b/packages/react-native/React/CoreModules/RCTDevSettings.mm @@ -10,6 +10,7 @@ #import #import +#import #import #import #import @@ -157,6 +158,11 @@ + (BOOL)requiresMainQueueSetup return NO; } +- (BOOL)_isBridgeMode +{ + return [self.bridge isKindOfClass:[RCTBridge class]]; +} + - (instancetype)initWithDataSource:(id)dataSource { if (self = [super init]) { @@ -177,7 +183,7 @@ - (instancetype)initWithDataSource:(id)dataSource - (void)initialize { #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION - if (self.bridge) { + if ([self _isBridgeMode]) { RCTBridge *__weak weakBridge = self.bridge; _bridgeExecutorOverrideToken = [[RCTPackagerConnection sharedPackagerConnection] addNotificationHandler:^(id params) { @@ -208,7 +214,7 @@ - (void)initialize #endif #if RCT_ENABLE_INSPECTOR - if (self.bridge) { + if ([self _isBridgeMode]) { // We need this dispatch to the main thread because the bridge is not yet // finished with its initialisation. By the time it relinquishes control of // the main thread, this operation can be performed. @@ -249,7 +255,7 @@ - (void)invalidate { [super invalidate]; #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION - if (self.bridge) { + if ([self _isBridgeMode]) { [[RCTPackagerConnection sharedPackagerConnection] removeHandler:_bridgeExecutorOverrideToken]; } @@ -280,7 +286,7 @@ - (id)settingForKey:(NSString *)key - (BOOL)isDeviceDebuggingAvailable { #if RCT_ENABLE_INSPECTOR - if (self.bridge) { + if ([self _isBridgeMode]) { return self.bridge.isInspectable; } else { return self.isInspectable; diff --git a/packages/react-native/React/CoreModules/RCTDeviceInfo.mm b/packages/react-native/React/CoreModules/RCTDeviceInfo.mm index bfb49700825841..573c0c320a0c6e 100644 --- a/packages/react-native/React/CoreModules/RCTDeviceInfo.mm +++ b/packages/react-native/React/CoreModules/RCTDeviceInfo.mm @@ -54,11 +54,6 @@ - (void)initialize _currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(interfaceOrientationDidChange) - name:UIApplicationDidChangeStatusBarOrientationNotification - object:nil]; - _currentInterfaceDimensions = [self _exportedDimensions]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -75,6 +70,10 @@ - (void)initialize selector:@selector(interfaceFrameDidChange) name:RCTWindowFrameDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(interfaceFrameDidChange) + name:UIDeviceOrientationDidChangeNotification + object:nil]; // TODO T175901725 - Registering the RCTDeviceInfo module to the notification is a short-term fix to unblock 0.73 // The actual behavior should be that the module is properly registered in the TurboModule/Bridge infrastructure @@ -89,6 +88,9 @@ - (void)initialize - (void)invalidate { + if (_invalidated) { + return; + } _invalidated = YES; [self _cleanupObservers]; } @@ -99,20 +101,15 @@ - (void)_cleanupObservers name:RCTAccessibilityManagerDidUpdateMultiplierNotification object:[_moduleRegistry moduleForName:"AccessibilityManager"]]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIApplicationDidChangeStatusBarOrientationNotification - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:RCTUserInterfaceStyleDidChangeNotification object:nil]; [[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]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; } static BOOL RCTIsIPhoneNotched() diff --git a/packages/react-native/React/CoreModules/RCTRedBox.mm b/packages/react-native/React/CoreModules/RCTRedBox.mm index 822e17ab570baf..15cf2f023aaa4c 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox.mm +++ b/packages/react-native/React/CoreModules/RCTRedBox.mm @@ -274,7 +274,13 @@ - (void)dismiss - (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 diff --git a/packages/react-native/React/CxxBridge/RCTCxxBridge.mm b/packages/react-native/React/CxxBridge/RCTCxxBridge.mm index a611708814e2a1..05278d5572547c 100644 --- a/packages/react-native/React/CxxBridge/RCTCxxBridge.mm +++ b/packages/react-native/React/CxxBridge/RCTCxxBridge.mm @@ -9,6 +9,7 @@ #include #import +#import #import #import #import diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm index eb81a151c3fc41..c7d0a525a9ac74 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm @@ -7,18 +7,22 @@ #import "RCTRootComponentView.h" +#import #import #import #import "RCTConversions.h" using namespace facebook::react; -@implementation RCTRootComponentView +@implementation RCTRootComponentView { + BOOL _contentHasAppeared; +} - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { _props = RootShadowNode::defaultSharedProps(); + _contentHasAppeared = NO; } return self; @@ -26,6 +30,23 @@ - (instancetype)initWithFrame:(CGRect)frame #pragma mark - RCTComponentViewProtocol +- (void)prepareForRecycle +{ + [super prepareForRecycle]; + _contentHasAppeared = NO; +} + +- (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +{ + [super mountChildComponentView:childComponentView index:index]; + if (!self->_contentHasAppeared) { + self->_contentHasAppeared = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:RCTContentDidAppearNotification object:self]; + }); + } +} + + (ComponentDescriptorProvider)componentDescriptorProvider { return concreteComponentDescriptorProvider(); diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 90a9a134dcea72..547dd819a74ea0 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -122,8 +122,8 @@ - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { _props = ScrollViewShadowNode::defaultSharedProps(); - _scrollView = [[RCTEnhancedScrollView alloc] initWithFrame:self.bounds]; + _scrollView.clipsToBounds = _props->getClipsContentToBounds(); _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _scrollView.delaysContentTouches = NO; ((RCTEnhancedScrollView *)_scrollView).overridingDelegate = self; @@ -253,6 +253,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & } } + // Overflow prop + if (oldScrollViewProps.getClipsContentToBounds() != newScrollViewProps.getClipsContentToBounds()) { + _scrollView.clipsToBounds = newScrollViewProps.getClipsContentToBounds(); + } + MAP_SCROLL_VIEW_PROP(zoomScale); if (oldScrollViewProps.contentInset != newScrollViewProps.contentInset) { @@ -420,6 +425,11 @@ - (void)_updateStateWithContentOffset - (void)prepareForRecycle { + [super prepareForRecycle]; + // Must invalidate state before setting contentOffset on ScrollView. + // Otherwise the state will be propagated to shadow tree. + _state.reset(); + const auto &props = static_cast(*_props); _scrollView.contentOffset = RCTCGPointFromPoint(props.contentOffset); // We set the default behavior to "never" so that iOS @@ -427,7 +437,6 @@ - (void)prepareForRecycle // and keeps it as an opt-in behavior. _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; _shouldUpdateContentInsetAdjustmentBehavior = YES; - _state.reset(); _isUserTriggeredScrolling = NO; CGRect oldFrame = self.frame; self.frame = CGRectZero; diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 44e74da5cea06f..b371ccbe6bfff3 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -594,6 +594,9 @@ - (void)_setAttributedString:(NSAttributedString *)attributedString UITextRange *selectedRange = _backedTextInputView.selectedTextRange; NSInteger oldTextLength = _backedTextInputView.attributedText.string.length; _backedTextInputView.attributedText = attributedString; + // Updating the UITextView attributedText, for example changing the lineHeight, the color or adding + // a new paragraph with \n, causes the cursor to move to the end of the Text and scroll. + // This is fixed by restoring the cursor position and scrolling to that position (iOS issue 652653). if (selectedRange.empty) { // Maintaining a cursor position relative to the end of the old text. NSInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument @@ -604,6 +607,7 @@ - (void)_setAttributedString:(NSAttributedString *)attributedString offset:newOffset]; [_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position] notifyDelegate:YES]; + [_backedTextInputView scrollRangeToVisible:NSMakeRange(offsetStart, 0)]; } [self _restoreTextSelection]; _lastStringStateWasUpdatedWith = attributedString; diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index a015c450ee21b7..8026a4bafc7635 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -257,6 +257,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & 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 @@ - (void)invalidateLayer 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() && @@ -601,8 +633,9 @@ - (void)invalidateLayer // iOS draws borders in front of the content whereas CSS draws them behind // the content. For this reason, only use iOS border drawing when clipping // or when the border is hidden. - borderMetrics.borderWidths.left == 0 || - colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 || self.clipsToBounds); + borderMetrics.borderWidths.left == 0 || self.clipsToBounds || + (colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 && + (*borderMetrics.borderColors.left).getUIColor() != nullptr)); CGColorRef backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor; diff --git a/packages/react-native/React/Fabric/RCTScheduler.h b/packages/react-native/React/Fabric/RCTScheduler.h index 888770cfd9fea0..f80e83c7a4cbad 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.h +++ b/packages/react-native/React/Fabric/RCTScheduler.h @@ -30,6 +30,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)schedulerDidFinishTransaction:(facebook::react::MountingCoordinator::Shared)mountingCoordinator; +- (void)schedulerShouldRenderTransactions:(facebook::react::MountingCoordinator::Shared)mountingCoordinator; + - (void)schedulerDidDispatchCommand:(const facebook::react::ShadowView &)shadowView commandName:(const std::string &)commandName args:(const folly::dynamic &)args; diff --git a/packages/react-native/React/Fabric/RCTScheduler.mm b/packages/react-native/React/Fabric/RCTScheduler.mm index 3e774560998eb1..91e1225c67a84b 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.mm +++ b/packages/react-native/React/Fabric/RCTScheduler.mm @@ -30,6 +30,12 @@ void schedulerDidFinishTransaction(const MountingCoordinator::Shared &mountingCo [scheduler.delegate schedulerDidFinishTransaction:mountingCoordinator]; } + void schedulerShouldRenderTransactions(const MountingCoordinator::Shared &mountingCoordinator) override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler.delegate schedulerShouldRenderTransactions:mountingCoordinator]; + } + void schedulerDidRequestPreliminaryViewAllocation(SurfaceId surfaceId, const ShadowNode &shadowNode) override { // Does nothing. diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm index 4c1e048c4063f2..80ff067dc4ab37 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm @@ -354,6 +354,11 @@ - (void)_applicationWillTerminate #pragma mark - RCTSchedulerDelegate - (void)schedulerDidFinishTransaction:(MountingCoordinator::Shared)mountingCoordinator +{ + // no-op, we will flush the transaction from schedulerShouldRenderTransactions +} + +- (void)schedulerShouldRenderTransactions:(MountingCoordinator::Shared)mountingCoordinator { [_mountingManager scheduleTransaction:mountingCoordinator]; } diff --git a/packages/react-native/React/Modules/RCTUIManager.m b/packages/react-native/React/Modules/RCTUIManager.m index f5e5c7c7a82aca..1a1b1155c3322e 100644 --- a/packages/react-native/React/Modules/RCTUIManager.m +++ b/packages/react-native/React/Modules/RCTUIManager.m @@ -1674,35 +1674,46 @@ - (instancetype)initWithUIManager:(RCTUIManager *)uiManager andRegistry:(NSDicti { self = [super init]; if (self) { - self->_uiManager = uiManager; - self->_registry = registry; + _uiManager = uiManager; + _registry = registry; } return self; } +- (NSUInteger)count +{ + return self->_registry.count; +} + +- (NSEnumerator *)keyEnumerator +{ + return self->_registry.keyEnumerator; +} + - (id)objectForKey:(id)key { if (![key isKindOfClass:[NSNumber class]]) { - return [super objectForKeyedSubscript:key]; + return NULL; } NSNumber *index = (NSNumber *)key; - UIView *view = [_uiManager viewForReactTag:index]; + UIView *view = _registry[index]; if (view) { return [RCTUIManager paperViewOrCurrentView:view]; } - view = _registry[index]; + view = [_uiManager viewForReactTag:index]; if (view) { return [RCTUIManager paperViewOrCurrentView:view]; } - return [super objectForKeyedSubscript:key]; + return NULL; } - (void)removeObjectForKey:(id)key { if (![key isKindOfClass:[NSNumber class]]) { - return [super removeObjectForKey:key]; + return; } + NSNumber *tag = (NSNumber *)key; if (_registry[key]) { @@ -1710,8 +1721,6 @@ - (void)removeObjectForKey:(id)key [mutableRegistry removeObjectForKey:tag]; } else if ([_uiManager viewForReactTag:tag]) { [_uiManager removeViewFromRegistry:tag]; - } else { - [super removeObjectForKey:key]; } } diff --git a/packages/react-native/React/Views/RCTComponentData.m b/packages/react-native/React/Views/RCTComponentData.m index db6f971142624f..3952d177e66063 100644 --- a/packages/react-native/React/Views/RCTComponentData.m +++ b/packages/react-native/React/Views/RCTComponentData.m @@ -416,7 +416,20 @@ - (void)setProps:(NSDictionary *)props forView:(id + (NSDictionary *)constantsForViewMangerClass:(Class)managerClass { if ([managerClass instancesRespondToSelector:@selector(constantsToExport)]) { - return [[managerClass new] constantsToExport]; + BOOL shouldRunOnMainThread = NO; + + if ([managerClass respondsToSelector:@selector(requiresMainQueueSetup)]) { + shouldRunOnMainThread = [managerClass requiresMainQueueSetup]; + } + if (shouldRunOnMainThread) { + __block NSDictionary *constants; + RCTUnsafeExecuteOnMainQueueSync(^{ + constants = [[managerClass new] constantsToExport]; + }); + return constants; + } else { + return [[managerClass new] constantsToExport]; + } } return @{}; } diff --git a/packages/react-native/React/Views/RCTCursor.h b/packages/react-native/React/Views/RCTCursor.h new file mode 100644 index 00000000000000..63fcb3e123e390 --- /dev/null +++ b/packages/react-native/React/Views/RCTCursor.h @@ -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 + +typedef NS_ENUM(NSInteger, RCTCursor) { + RCTCursorAuto, + RCTCursorPointer, +}; diff --git a/packages/react-native/React/Views/RCTView.h b/packages/react-native/React/Views/RCTView.h index 200d8b451bf59e..8abda6e8538d79 100644 --- a/packages/react-native/React/Views/RCTView.h +++ b/packages/react-native/React/Views/RCTView.h @@ -10,6 +10,7 @@ #import #import #import +#import #import 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. */ diff --git a/packages/react-native/React/Views/RCTView.m b/packages/react-native/React/Views/RCTView.m index 6e6f9cd2761035..3a8658100332fe 100644 --- a/packages/react-native/React/Views/RCTView.m +++ b/packages/react-native/React/Views/RCTView.m @@ -136,6 +136,7 @@ - (instancetype)initWithFrame:(CGRect)frame _borderCurve = RCTBorderCurveCircular; _borderStyle = RCTBorderStyleSolid; _hitTestEdgeInsets = UIEdgeInsetsZero; + _cursor = RCTCursorAuto; _backgroundColor = super.backgroundColor; } @@ -796,6 +797,8 @@ - (void)displayLayer:(CALayer *)layer 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; diff --git a/packages/react-native/React/Views/RCTViewManager.m b/packages/react-native/React/Views/RCTViewManager.m index 66c449d7e81a00..0a0126545bde5f 100644 --- a/packages/react-native/React/Views/RCTViewManager.m +++ b/packages/react-native/React/Views/RCTViewManager.m @@ -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 @@ - (RCTShadowView *)shadowView 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) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 24271f239edd3a..0f8294bec0637a 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -29,6 +29,15 @@ public class com/facebook/react/CoreModulesPackage$$ReactModuleInfoProvider : co public fun getReactModuleInfos ()Ljava/util/Map; } +public class com/facebook/react/DebugCorePackage : com/facebook/react/TurboReactPackage, com/facebook/react/ViewManagerOnDemandReactPackage { + public fun ()V + public fun createViewManager (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/lang/String;)Lcom/facebook/react/uimanager/ViewManager; + public fun getModule (Ljava/lang/String;Lcom/facebook/react/bridge/ReactApplicationContext;)Lcom/facebook/react/bridge/NativeModule; + public fun getReactModuleInfoProvider ()Lcom/facebook/react/module/model/ReactModuleInfoProvider; + public fun getViewManagerNames (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/Collection; + public fun getViewManagers (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/List; +} + public class com/facebook/react/DebugCorePackage$$ReactModuleInfoProvider : com/facebook/react/module/model/ReactModuleInfoProvider { public fun ()V public fun getReactModuleInfos ()Ljava/util/Map; @@ -83,6 +92,7 @@ public abstract class com/facebook/react/ReactActivity : androidx/appcompat/app/ protected fun ()V protected fun createReactActivityDelegate ()Lcom/facebook/react/ReactActivityDelegate; protected fun getMainComponentName ()Ljava/lang/String; + public fun getReactDelegate ()Lcom/facebook/react/ReactDelegate; protected final fun getReactInstanceManager ()Lcom/facebook/react/ReactInstanceManager; protected final fun getReactNativeHost ()Lcom/facebook/react/ReactNativeHost; public fun invokeDefaultOnBackPressed ()V @@ -113,6 +123,7 @@ public class com/facebook/react/ReactActivityDelegate { protected fun getLaunchOptions ()Landroid/os/Bundle; public fun getMainComponentName ()Ljava/lang/String; protected fun getPlainActivity ()Landroid/app/Activity; + protected fun getReactDelegate ()Lcom/facebook/react/ReactDelegate; public fun getReactHost ()Lcom/facebook/react/ReactHost; public fun getReactInstanceManager ()Lcom/facebook/react/ReactInstanceManager; protected fun getReactNativeHost ()Lcom/facebook/react/ReactNativeHost; @@ -151,9 +162,15 @@ public class com/facebook/react/ReactDelegate { public fun loadApp (Ljava/lang/String;)V public fun onActivityResult (IILandroid/content/Intent;Z)V public fun onBackPressed ()Z + public fun onConfigurationChanged (Landroid/content/res/Configuration;)V public fun onHostDestroy ()V public fun onHostPause ()V public fun onHostResume ()V + public fun onKeyDown (ILandroid/view/KeyEvent;)Z + public fun onKeyLongPress (I)Z + public fun onNewIntent (Landroid/content/Intent;)Z + public fun onWindowFocusChanged (Z)V + public fun reload ()V public fun shouldShowDevMenuOrReload (ILandroid/view/KeyEvent;)Z } @@ -196,13 +213,17 @@ public abstract interface class com/facebook/react/ReactHost { public abstract fun getJsEngineResolutionAlgorithm ()Lcom/facebook/react/JSEngineResolutionAlgorithm; public abstract fun getLifecycleState ()Lcom/facebook/react/common/LifecycleState; public abstract fun getReactQueueConfiguration ()Lcom/facebook/react/bridge/queue/ReactQueueConfiguration; + public abstract fun onActivityResult (Landroid/app/Activity;IILandroid/content/Intent;)V public abstract fun onBackPressed ()Z + public abstract fun onConfigurationChanged (Landroid/content/Context;)V public abstract fun onHostDestroy ()V public abstract fun onHostDestroy (Landroid/app/Activity;)V public abstract fun onHostPause ()V public abstract fun onHostPause (Landroid/app/Activity;)V public abstract fun onHostResume (Landroid/app/Activity;)V public abstract fun onHostResume (Landroid/app/Activity;Lcom/facebook/react/modules/core/DefaultHardwareBackBtnHandler;)V + public abstract fun onNewIntent (Landroid/content/Intent;)V + public abstract fun onWindowFocusChange (Z)V public abstract fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface; public abstract fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V public abstract fun setJsEngineResolutionAlgorithm (Lcom/facebook/react/JSEngineResolutionAlgorithm;)V @@ -322,7 +343,7 @@ public abstract class com/facebook/react/ReactPackageTurboModuleManagerDelegate protected fun (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/util/List;Lcom/facebook/jni/HybridData;)V public fun getEagerInitModuleNames ()Ljava/util/List; public fun getLegacyModule (Ljava/lang/String;)Lcom/facebook/react/bridge/NativeModule; - public fun getModule (Ljava/lang/String;)Lcom/facebook/react/internal/turbomodule/core/interfaces/TurboModule; + public fun getModule (Ljava/lang/String;)Lcom/facebook/react/turbomodule/core/interfaces/TurboModule; public fun unstable_enableSyncVoidMethods ()Z public fun unstable_isLegacyModuleRegistered (Ljava/lang/String;)Z public fun unstable_isModuleRegistered (Ljava/lang/String;)Z @@ -949,6 +970,10 @@ public abstract class com/facebook/react/bridge/NativeArray : com/facebook/react public fun toString ()Ljava/lang/String; } +public abstract interface class com/facebook/react/bridge/NativeArrayInterface { + public abstract fun toString ()Ljava/lang/String; +} + public abstract class com/facebook/react/bridge/NativeMap { public fun (Lcom/facebook/jni/HybridData;)V public fun toString ()Ljava/lang/String; @@ -2511,7 +2536,7 @@ public class com/facebook/react/fabric/FabricSoLoader { public static fun staticInit ()V } -public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/bridge/UIManager { +public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/bridge/UIManager, com/facebook/react/fabric/interop/UIBlockViewResolver { public static final field ENABLE_FABRIC_LOGS Z public static final field ENABLE_FABRIC_PERF_LOGS Z public static final field IS_DEVELOPMENT_ENVIRONMENT Z @@ -2856,6 +2881,7 @@ public class com/facebook/react/module/model/ReactModuleInfo { public fun (Ljava/lang/String;Ljava/lang/String;ZZZZ)V public fun (Ljava/lang/String;Ljava/lang/String;ZZZZZ)V public fun canOverrideExistingModule ()Z + public static fun classIsTurboModule (Ljava/lang/Class;)Z public fun className ()Ljava/lang/String; public fun hasConstants ()Z public fun isCxxModule ()Z @@ -3199,7 +3225,7 @@ public class com/facebook/react/modules/dialog/DialogModule : com/facebook/fbrea public fun showAlert (Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V } -public class com/facebook/react/modules/fresco/FrescoModule : com/facebook/react/bridge/ReactContextBaseJavaModule, com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/internal/turbomodule/core/interfaces/TurboModule, com/facebook/react/modules/common/ModuleDataCleaner$Cleanable { +public class com/facebook/react/modules/fresco/FrescoModule : com/facebook/react/bridge/ReactContextBaseJavaModule, com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/modules/common/ModuleDataCleaner$Cleanable, com/facebook/react/turbomodule/core/interfaces/TurboModule { public static final field NAME Ljava/lang/String; public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/imagepipeline/core/ImagePipeline;Z)V @@ -3440,7 +3466,7 @@ public class com/facebook/react/modules/systeminfo/AndroidInfoHelpers { public static fun getServerHost (Ljava/lang/Integer;)Ljava/lang/String; } -public class com/facebook/react/modules/systeminfo/AndroidInfoModule : com/facebook/fbreact/specs/NativePlatformConstantsAndroidSpec, com/facebook/react/internal/turbomodule/core/interfaces/TurboModule { +public class com/facebook/react/modules/systeminfo/AndroidInfoModule : com/facebook/fbreact/specs/NativePlatformConstantsAndroidSpec, com/facebook/react/turbomodule/core/interfaces/TurboModule { public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun getAndroidID ()Ljava/lang/String; public fun getTypedExportedConstants ()Ljava/util/Map; @@ -3574,6 +3600,43 @@ public abstract class com/facebook/react/runtime/BindingsInstaller { public fun (Lcom/facebook/jni/HybridData;)V } +public final class com/facebook/react/runtime/BridgelessCatalystInstance : com/facebook/react/bridge/CatalystInstance { + public fun (Lcom/facebook/react/runtime/ReactHostImpl;)V + public fun addBridgeIdleDebugListener (Lcom/facebook/react/bridge/NotThreadSafeBridgeIdleDebugListener;)V + public fun addJSIModules (Ljava/util/List;)V + public fun callFunction (Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/bridge/NativeArray;)V + public fun destroy ()V + public fun extendNativeModules (Lcom/facebook/react/bridge/NativeModuleRegistry;)V + public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager; + public fun getJSCallInvokerHolder ()Lcom/facebook/react/turbomodule/core/interfaces/CallInvokerHolder; + public fun getJSIModule (Lcom/facebook/react/bridge/JSIModuleType;)Lcom/facebook/react/bridge/JSIModule; + public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule; + public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder; + public fun getNativeMethodCallInvokerHolder ()Lcom/facebook/react/turbomodule/core/interfaces/NativeMethodCallInvokerHolder; + public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule; + public fun getNativeModule (Ljava/lang/String;)Lcom/facebook/react/bridge/NativeModule; + public fun getNativeModules ()Ljava/util/Collection; + public fun getReactQueueConfiguration ()Lcom/facebook/react/bridge/queue/ReactQueueConfiguration; + public fun getRuntimeExecutor ()Lcom/facebook/react/bridge/RuntimeExecutor; + public fun getRuntimeScheduler ()Lcom/facebook/react/bridge/RuntimeScheduler; + public fun getSourceURL ()Ljava/lang/String; + public fun handleMemoryPressure (I)V + public fun hasNativeModule (Ljava/lang/Class;)Z + public fun hasRunJSBundle ()Z + public fun invokeCallback (ILcom/facebook/react/bridge/NativeArrayInterface;)V + public fun isDestroyed ()Z + public fun loadScriptFromAssets (Landroid/content/res/AssetManager;Ljava/lang/String;Z)V + public fun loadScriptFromFile (Ljava/lang/String;Ljava/lang/String;Z)V + public fun loadSplitBundleFromFile (Ljava/lang/String;Ljava/lang/String;)V + public fun registerSegment (ILjava/lang/String;)V + public fun removeBridgeIdleDebugListener (Lcom/facebook/react/bridge/NotThreadSafeBridgeIdleDebugListener;)V + public fun runJSBundle ()V + public fun setFabricUIManager (Lcom/facebook/react/bridge/UIManager;)V + public fun setSourceURLs (Ljava/lang/String;Ljava/lang/String;)V + public fun setTurboModuleManager (Lcom/facebook/react/bridge/JSIModule;)V + public fun setTurboModuleRegistry (Lcom/facebook/react/internal/turbomodule/core/interfaces/TurboModuleRegistry;)V +} + public class com/facebook/react/runtime/CoreReactPackage$$ReactModuleInfoProvider : com/facebook/react/module/model/ReactModuleInfoProvider { public fun ()V public fun getReactModuleInfos ()Ljava/util/Map; @@ -3601,13 +3664,17 @@ public class com/facebook/react/runtime/ReactHostImpl : com/facebook/react/React public fun getLifecycleState ()Lcom/facebook/react/common/LifecycleState; public fun getMemoryPressureRouter ()Lcom/facebook/react/MemoryPressureRouter; public fun getReactQueueConfiguration ()Lcom/facebook/react/bridge/queue/ReactQueueConfiguration; + public fun onActivityResult (Landroid/app/Activity;IILandroid/content/Intent;)V public fun onBackPressed ()Z + public fun onConfigurationChanged (Landroid/content/Context;)V public fun onHostDestroy ()V public fun onHostDestroy (Landroid/app/Activity;)V public fun onHostPause ()V public fun onHostPause (Landroid/app/Activity;)V public fun onHostResume (Landroid/app/Activity;)V public fun onHostResume (Landroid/app/Activity;Lcom/facebook/react/modules/core/DefaultHardwareBackBtnHandler;)V + public fun onNewIntent (Landroid/content/Intent;)V + public fun onWindowFocusChange (Z)V public fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface; public fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V public fun removeReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V @@ -3806,7 +3873,9 @@ public abstract interface class com/facebook/react/turbomodule/core/interfaces/C public abstract interface class com/facebook/react/turbomodule/core/interfaces/NativeMethodCallInvokerHolder { } -public abstract interface class com/facebook/react/turbomodule/core/interfaces/TurboModule : com/facebook/react/internal/turbomodule/core/interfaces/TurboModule { +public abstract interface class com/facebook/react/turbomodule/core/interfaces/TurboModule { + public abstract fun initialize ()V + public abstract fun invalidate ()V } public abstract class com/facebook/react/uimanager/BaseViewManager : com/facebook/react/uimanager/ViewManager, android/view/View$OnLayoutChangeListener, com/facebook/react/uimanager/BaseViewManagerInterface { @@ -4828,6 +4897,7 @@ public class com/facebook/react/uimanager/UIImplementation { public fun dispatchViewUpdates (I)V public fun findSubviewIn (IFFLcom/facebook/react/bridge/Callback;)V public fun getProfiledBatchPerfCounters ()Ljava/util/Map; + public fun getRootViewNum ()I protected fun handleCreateView (Lcom/facebook/react/uimanager/ReactShadowNode;ILcom/facebook/react/uimanager/ReactStylesDiffMap;)V protected fun handleUpdateView (Lcom/facebook/react/uimanager/ReactShadowNode;Ljava/lang/String;Lcom/facebook/react/uimanager/ReactStylesDiffMap;)V public fun manageChildren (ILcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V @@ -5673,17 +5743,6 @@ public abstract interface class com/facebook/react/viewmanagers/AndroidHorizonta public abstract fun setRemoveClippedSubviews (Landroid/view/View;Z)V } -public class com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V - public fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V - public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V -} - -public abstract interface class com/facebook/react/viewmanagers/AndroidPopupMenuManagerInterface { - public abstract fun setMenuItems (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public abstract fun show (Landroid/view/View;)V -} - public class com/facebook/react/viewmanagers/AndroidProgressBarManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V @@ -6153,47 +6212,6 @@ public abstract interface class com/facebook/react/views/modal/ReactModalHostVie public abstract fun onRequestClose (Landroid/content/DialogInterface;)V } -public final class com/facebook/react/views/popupmenu/PopupMenuSelectionEvent : com/facebook/react/uimanager/events/Event { - public static final field Companion Lcom/facebook/react/views/popupmenu/PopupMenuSelectionEvent$Companion; - public static final field EVENT_NAME Ljava/lang/String; - public fun (III)V - public fun dispatch (Lcom/facebook/react/uimanager/events/RCTEventEmitter;)V - public fun getEventName ()Ljava/lang/String; -} - -public final class com/facebook/react/views/popupmenu/PopupMenuSelectionEvent$Companion { -} - -public final class com/facebook/react/views/popupmenu/ReactPopupMenuContainer : android/widget/FrameLayout { - public fun (Landroid/content/Context;)V - public final fun setMenuItems (Lcom/facebook/react/bridge/ReadableArray;)V - public final fun showPopupMenu ()V -} - -public final class com/facebook/react/views/popupmenu/ReactPopupMenuManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/AndroidPopupMenuManagerInterface { - public static final field Companion Lcom/facebook/react/views/popupmenu/ReactPopupMenuManager$Companion; - public static final field REACT_CLASS Ljava/lang/String; - public fun ()V - public synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View; - public fun getName ()Ljava/lang/String; - public synthetic fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V - public fun receiveCommand (Lcom/facebook/react/views/popupmenu/ReactPopupMenuContainer;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V - public synthetic fun setMenuItems (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public fun setMenuItems (Lcom/facebook/react/views/popupmenu/ReactPopupMenuContainer;Lcom/facebook/react/bridge/ReadableArray;)V - public synthetic fun show (Landroid/view/View;)V - public fun show (Lcom/facebook/react/views/popupmenu/ReactPopupMenuContainer;)V -} - -public class com/facebook/react/views/popupmenu/ReactPopupMenuManager$$PropsSetter : com/facebook/react/uimanager/ViewManagerPropertyUpdater$ViewManagerSetter { - public fun ()V - public fun getProperties (Ljava/util/Map;)V - public synthetic fun setProperty (Lcom/facebook/react/uimanager/ViewManager;Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V - public fun setProperty (Lcom/facebook/react/views/popupmenu/ReactPopupMenuManager;Lcom/facebook/react/views/popupmenu/ReactPopupMenuContainer;Ljava/lang/String;Ljava/lang/Object;)V -} - -public final class com/facebook/react/views/popupmenu/ReactPopupMenuManager$Companion { -} - public class com/facebook/react/views/progressbar/ProgressBarShadowNode : com/facebook/react/uimanager/LayoutShadowNode, com/facebook/yoga/YogaMeasureFunction { public fun ()V public fun getStyle ()Ljava/lang/String; @@ -7091,6 +7109,7 @@ public class com/facebook/react/views/text/TextAttributeProps : com/facebook/rea public static final field TA_KEY_TEXT_SHADOW_OFFSET_DY S public static final field TA_KEY_TEXT_SHADOW_RADIUS S public static final field TA_KEY_TEXT_TRANSFORM S + public static final field UNSET I protected field mAccessibilityRole Lcom/facebook/react/uimanager/ReactAccessibilityDelegate$AccessibilityRole; protected field mAllowFontScaling Z protected field mBackgroundColor I @@ -7678,4 +7697,3 @@ public class com/facebook/react/views/view/ViewGroupClickEvent : com/facebook/re protected fun getEventData ()Lcom/facebook/react/bridge/WritableMap; public fun getEventName ()Ljava/lang/String; } - diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index 7e0640a0e50a66..4909aeddba5dd2 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -125,6 +125,26 @@ val preparePrefab by "react/renderer/components/view/"), Pair("../ReactCommon/react/renderer/components/view/platform/android/", ""), )), + PrefabPreprocessingEntry( + "rrc_text", + listOf( + Pair( + "../ReactCommon/react/renderer/components/text/", + "react/renderer/components/text/"), + Pair( + "../ReactCommon/react/renderer/attributedstring", + "react/renderer/attributedstring"), + )), + PrefabPreprocessingEntry( + "rrc_textinput", + listOf( + Pair( + "../ReactCommon/react/renderer/components/textinput/", + "react/renderer/components/textinput/"), + Pair( + "../ReactCommon/react/renderer/components/textinput/platform/android/", + ""), + )), PrefabPreprocessingEntry( "rrc_legacyviewmanagerinterop", Pair( @@ -138,6 +158,14 @@ val preparePrefab by PrefabPreprocessingEntry( "react_render_mapbuffer", Pair("../ReactCommon/react/renderer/mapbuffer/", "react/renderer/mapbuffer/")), + PrefabPreprocessingEntry( + "react_render_textlayoutmanager", + listOf( + Pair( + "../ReactCommon/react/renderer/textlayoutmanager/", + "react/renderer/textlayoutmanager/"), + Pair("../ReactCommon/react/renderer/textlayoutmanager/platform/android/", ""), + )), PrefabPreprocessingEntry( "yoga", listOf( @@ -470,6 +498,8 @@ android { } if (rootProject.hasProperty("ndkVersion") && rootProject.properties["ndkVersion"] != null) { ndkVersion = rootProject.properties["ndkVersion"].toString() + } else { + ndkVersion = libs.versions.ndkVersion.get() } compileOptions { @@ -538,11 +568,14 @@ android { "rrc_image", "rrc_root", "rrc_view", + "rrc_text", + "rrc_textinput", "rrc_legacyviewmanagerinterop", "jsi", "glog", "fabricjni", "react_render_mapbuffer", + "react_render_textlayoutmanager", "yoga", "folly_runtime", "react_nativemodule_core", @@ -662,6 +695,8 @@ android { create("rrc_image") { headers = File(prefabHeadersDir, "rrc_image").absolutePath } create("rrc_root") { headers = File(prefabHeadersDir, "rrc_root").absolutePath } create("rrc_view") { headers = File(prefabHeadersDir, "rrc_view").absolutePath } + create("rrc_text") { headers = File(prefabHeadersDir, "rrc_text").absolutePath } + create("rrc_textinput") { headers = File(prefabHeadersDir, "rrc_textinput").absolutePath } create("rrc_legacyviewmanagerinterop") { headers = File(prefabHeadersDir, "rrc_legacyviewmanagerinterop").absolutePath } @@ -671,6 +706,9 @@ android { create("react_render_mapbuffer") { headers = File(prefabHeadersDir, "react_render_mapbuffer").absolutePath } + create("react_render_textlayoutmanager") { + headers = File(prefabHeadersDir, "react_render_textlayoutmanager").absolutePath + } create("yoga") { headers = File(prefabHeadersDir, "yoga").absolutePath } create("folly_runtime") { headers = File(prefabHeadersDir, "folly_runtime").absolutePath } create("react_nativemodule_core") { diff --git a/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake b/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake index 1ad0af8843dbc1..e2efc266ca0f68 100644 --- a/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake +++ b/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake @@ -74,10 +74,13 @@ add_library(react_cxxreactpackage ALIAS ReactAndroid::react_cxxreactpackage) add_library(react_render_core ALIAS ReactAndroid::react_render_core) add_library(react_render_graphics ALIAS ReactAndroid::react_render_graphics) add_library(rrc_view ALIAS ReactAndroid::rrc_view) +add_library(rrc_text ALIAS ReactAndroid::rrc_text) +add_library(rrc_textinput ALIAS ReactAndroid::rrc_textinput) add_library(jsi ALIAS ReactAndroid::jsi) add_library(glog ALIAS ReactAndroid::glog) add_library(fabricjni ALIAS ReactAndroid::fabricjni) add_library(react_render_mapbuffer ALIAS ReactAndroid::react_render_mapbuffer) +add_library(react_render_textlayoutmanager ALIAS ReactAndroid::react_render_textlayoutmanager) add_library(yoga ALIAS ReactAndroid::yoga) add_library(folly_runtime ALIAS ReactAndroid::folly_runtime) add_library(react_nativemodule_core ALIAS ReactAndroid::react_nativemodule_core) @@ -106,8 +109,11 @@ target_link_libraries(${CMAKE_PROJECT_NAME} react_render_graphics # prefab ready react_render_imagemanager # prefab ready react_render_mapbuffer # prefab ready + react_render_textlayoutmanager # prefab ready rrc_image # prefab ready rrc_view # prefab ready + rrc_text # prefab ready + rrc_textinput # prefab ready rrc_legacyviewmanagerinterop # prefab ready runtimeexecutor # prefab ready turbomodulejsijni # prefab ready diff --git a/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp b/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp index d206b123ad44bb..56a38374370ab6 100644 --- a/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp +++ b/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp @@ -62,8 +62,16 @@ void registerComponents( std::shared_ptr cxxModuleProvider( const std::string& name, const std::shared_ptr& jsInvoker) { - // Not implemented yet: provide pure-C++ NativeModules here. - return nullptr; + // Here you can provide your CXX Turbo Modules coming from + // either your application or from external libraries. The approach to follow + // is similar to the following (for a module called `NativeCxxModuleExample`): + // + // if (name == NativeCxxModuleExample::kModuleName) { + // return std::make_shared(jsInvoker); + // } + + // And we fallback to the CXX module providers autolinked by RN CLI + return rncli_cxxModuleProvider(name, jsInvoker); } std::shared_ptr javaModuleProvider( diff --git a/packages/react-native/ReactAndroid/gradle.properties b/packages/react-native/ReactAndroid/gradle.properties index 31a4a4d071d617..7d82024e6fb6bb 100644 --- a/packages/react-native/ReactAndroid/gradle.properties +++ b/packages/react-native/ReactAndroid/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1000.0.0 +VERSION_NAME=0.74.2 react.internal.publishingGroup=com.facebook.react android.useAndroidX=true diff --git a/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts b/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts index 17eb24f355b55b..7fa719f08e60d2 100644 --- a/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts +++ b/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts @@ -192,6 +192,8 @@ android { } if (rootProject.hasProperty("ndkVersion") && rootProject.properties["ndkVersion"] != null) { ndkVersion = rootProject.properties["ndkVersion"].toString() + } else { + ndkVersion = libs.versions.ndkVersion.get() } defaultConfig { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java index 3ffbc8aa28d22d..ae1bb0131fd22e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java @@ -17,7 +17,6 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.devsupport.LogBoxModule; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModuleList; import com.facebook.react.module.model.ReactModuleInfo; @@ -115,7 +114,7 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { reactModule.canOverrideExistingModule(), reactModule.needsEagerInit(), reactModule.isCxxModule(), - TurboModule.class.isAssignableFrom(moduleClass))); + ReactModuleInfo.classIsTurboModule(moduleClass))); } return () -> reactModuleInfoMap; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java index 69471937ab8501..b695c937dbc63e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/DebugCorePackage.java @@ -12,7 +12,6 @@ import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.devsupport.JSCHeapCapture; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModuleList; import com.facebook.react.module.model.ReactModuleInfo; @@ -37,7 +36,7 @@ JSCHeapCapture.class, }) /* package */ -class DebugCorePackage extends TurboReactPackage implements ViewManagerOnDemandReactPackage { +public class DebugCorePackage extends TurboReactPackage implements ViewManagerOnDemandReactPackage { private @Nullable Map mViewManagers; public DebugCorePackage() {} @@ -74,7 +73,7 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { reactModule.canOverrideExistingModule(), reactModule.needsEagerInit(), reactModule.isCxxModule(), - TurboModule.class.isAssignableFrom(moduleClass))); + ReactModuleInfo.classIsTurboModule(moduleClass))); } return () -> reactModuleInfoMap; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java index e6fd3fdd534bb6..95862974324623 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java @@ -65,6 +65,10 @@ protected void onDestroy() { mDelegate.onDestroy(); } + public @Nullable ReactDelegate getReactDelegate() { + return mDelegate.getReactDelegate(); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java index 87ffae6ef54d01..a64876ff900ffd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java @@ -88,6 +88,10 @@ public ReactHost getReactHost() { return ((ReactApplication) getPlainActivity().getApplication()).getReactHost(); } + protected @Nullable ReactDelegate getReactDelegate() { + return mReactDelegate; + } + public ReactInstanceManager getReactInstanceManager() { return mReactDelegate.getReactInstanceManager(); } @@ -105,7 +109,11 @@ protected void onCreate(Bundle savedInstanceState) { } else { mReactDelegate = new ReactDelegate( - getPlainActivity(), getReactNativeHost(), mainComponentName, launchOptions) { + getPlainActivity(), + getReactNativeHost(), + mainComponentName, + launchOptions, + isFabricEnabled()) { @Override protected ReactRootView createRootView() { return ReactActivityDelegate.this.createRootView(launchOptions); @@ -144,15 +152,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } public boolean onKeyDown(int keyCode, KeyEvent event) { - if (!ReactFeatureFlags.enableBridgelessArchitecture) { - if (getReactNativeHost().hasInstance() - && getReactNativeHost().getUseDeveloperSupport() - && keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { - event.startTracking(); - return true; - } - } - return false; + return mReactDelegate.onKeyDown(keyCode, event); } public boolean onKeyUp(int keyCode, KeyEvent event) { @@ -160,15 +160,7 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { } public boolean onKeyLongPress(int keyCode, KeyEvent event) { - if (!ReactFeatureFlags.enableBridgelessArchitecture) { - if (getReactNativeHost().hasInstance() - && getReactNativeHost().getUseDeveloperSupport() - && keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { - getReactNativeHost().getReactInstanceManager().showDevOptionsDialog(); - return true; - } - } - return false; + return mReactDelegate.onKeyLongPress(keyCode); } public boolean onBackPressed() { @@ -176,29 +168,15 @@ public boolean onBackPressed() { } public boolean onNewIntent(Intent intent) { - if (!ReactFeatureFlags.enableBridgelessArchitecture) { - if (getReactNativeHost().hasInstance()) { - getReactNativeHost().getReactInstanceManager().onNewIntent(intent); - return true; - } - } - return false; + return mReactDelegate.onNewIntent(intent); } public void onWindowFocusChanged(boolean hasFocus) { - if (!ReactFeatureFlags.enableBridgelessArchitecture) { - if (getReactNativeHost().hasInstance()) { - getReactNativeHost().getReactInstanceManager().onWindowFocusChange(hasFocus); - } - } + mReactDelegate.onWindowFocusChanged(hasFocus); } public void onConfigurationChanged(Configuration newConfig) { - if (!ReactFeatureFlags.enableBridgelessArchitecture) { - if (getReactNativeHost().hasInstance()) { - getReactInstanceManager().onConfigurationChanged(getContext(), newConfig); - } - } + mReactDelegate.onConfigurationChanged(newConfig); } public void requestPermissions( diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java index 2140f4a0a1ba53..5eb70f792928e0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java @@ -9,13 +9,17 @@ import android.app.Activity; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.view.KeyEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.config.ReactFeatureFlags; +import com.facebook.react.devsupport.DisabledDevSupportManager; import com.facebook.react.devsupport.DoubleTapReloadRecognizer; +import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.interfaces.fabric.ReactSurface; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; @@ -42,6 +46,15 @@ public class ReactDelegate { private boolean mFabricEnabled = false; + /** + * Do not use this constructor as it's not accounting for New Architecture at all. You should + * either use {@link ReactDelegate#ReactDelegate(Activity, ReactHost, String, Bundle)} if you're + * on bridgeless mode or {@link ReactDelegate#ReactDelegate(Activity, ReactNativeHost, String, + * Bundle, boolean)} and use the last parameter to toggle paper/fabric. + * + * @deprecated Use one of the other constructors instead to account for New Architecture. + */ + @Deprecated public ReactDelegate( Activity activity, ReactNativeHost reactNativeHost, @@ -80,6 +93,20 @@ public ReactDelegate( mReactNativeHost = reactNativeHost; } + @Nullable + private DevSupportManager getDevSupportManager() { + if (ReactFeatureFlags.enableBridgelessArchitecture + && mReactHost != null + && mReactHost.getDevSupportManager() != null) { + return mReactHost.getDevSupportManager(); + } else if (getReactNativeHost().hasInstance() + && getReactNativeHost().getReactInstanceManager() != null) { + return getReactNativeHost().getReactInstanceManager().getDevSupportManager(); + } else { + return null; + } + } + public void onHostResume() { if (ReactFeatureFlags.enableBridgelessArchitecture) { if (mActivity instanceof DefaultHardwareBackBtnHandler) { @@ -136,11 +163,23 @@ public boolean onBackPressed() { return false; } + public boolean onNewIntent(Intent intent) { + if (ReactFeatureFlags.enableBridgelessArchitecture) { + mReactHost.onNewIntent(intent); + return true; + } else { + if (getReactNativeHost().hasInstance()) { + getReactNativeHost().getReactInstanceManager().onNewIntent(intent); + return true; + } + } + return false; + } + public void onActivityResult( int requestCode, int resultCode, Intent data, boolean shouldForwardToReactInstance) { if (ReactFeatureFlags.enableBridgelessArchitecture) { - // TODO T156475655: Implement onActivityResult for Bridgeless - return; + mReactHost.onActivityResult(mActivity, requestCode, resultCode, data); } else { if (getReactNativeHost().hasInstance() && shouldForwardToReactInstance) { getReactNativeHost() @@ -150,6 +189,86 @@ public void onActivityResult( } } + public void onWindowFocusChanged(boolean hasFocus) { + if (ReactFeatureFlags.enableBridgelessArchitecture) { + mReactHost.onWindowFocusChange(hasFocus); + } else { + if (getReactNativeHost().hasInstance()) { + getReactNativeHost().getReactInstanceManager().onWindowFocusChange(hasFocus); + } + } + } + + public void onConfigurationChanged(Configuration newConfig) { + if (ReactFeatureFlags.enableBridgelessArchitecture) { + mReactHost.onConfigurationChanged(Assertions.assertNotNull(mActivity)); + } else { + if (getReactNativeHost().hasInstance()) { + getReactInstanceManager() + .onConfigurationChanged(Assertions.assertNotNull(mActivity), newConfig); + } + } + } + + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD + && ((ReactFeatureFlags.enableBridgelessArchitecture + && mReactHost != null + && mReactHost.getDevSupportManager() != null) + || (getReactNativeHost().hasInstance() + && getReactNativeHost().getUseDeveloperSupport()))) { + event.startTracking(); + return true; + } + return false; + } + + public boolean onKeyLongPress(int keyCode) { + if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { + if (ReactFeatureFlags.enableBridgelessArchitecture + && mReactHost != null + && mReactHost.getDevSupportManager() != null) { + mReactHost.getDevSupportManager().showDevOptionsDialog(); + return true; + } else { + if (getReactNativeHost().hasInstance() && getReactNativeHost().getUseDeveloperSupport()) { + getReactNativeHost().getReactInstanceManager().showDevOptionsDialog(); + return true; + } + } + } + return false; + } + + public void reload() { + DevSupportManager devSupportManager = getDevSupportManager(); + if (devSupportManager == null) { + return; + } + + // Reload in RELEASE mode + if (devSupportManager instanceof DisabledDevSupportManager) { + // Do not reload the bundle from JS as there is no bundler running in release mode. + if (ReactFeatureFlags.enableBridgelessArchitecture) { + if (mReactHost != null) { + mReactHost.reload("ReactDelegate.reload()"); + } + } else { + UiThreadUtil.runOnUiThread( + () -> { + if (mReactNativeHost.hasInstance() + && mReactNativeHost.getReactInstanceManager() != null) { + mReactNativeHost.getReactInstanceManager().recreateReactContextInBackground(); + } + }); + } + return; + } + + // Reload in DEBUG mode + devSupportManager.handleReloadJS(); + } + public void loadApp() { loadApp(mMainComponentName); } @@ -196,22 +315,21 @@ protected ReactRootView createRootView() { * application. */ public boolean shouldShowDevMenuOrReload(int keyCode, KeyEvent event) { - if (ReactFeatureFlags.enableBridgelessArchitecture) { - // TODO T156475655: Implement shouldShowDevMenuOrReload for Bridgeless + DevSupportManager devSupportManager = getDevSupportManager(); + if (devSupportManager == null) { return false; - } else if (getReactNativeHost().hasInstance() - && getReactNativeHost().getUseDeveloperSupport()) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - getReactNativeHost().getReactInstanceManager().showDevOptionsDialog(); - return true; - } - boolean didDoubleTapR = - Assertions.assertNotNull(mDoubleTapReloadRecognizer) - .didDoubleTapR(keyCode, mActivity.getCurrentFocus()); - if (didDoubleTapR) { - getReactNativeHost().getReactInstanceManager().getDevSupportManager().handleReloadJS(); - return true; - } + } + + if (keyCode == KeyEvent.KEYCODE_MENU) { + devSupportManager.showDevOptionsDialog(); + return true; + } + boolean didDoubleTapR = + Assertions.assertNotNull(mDoubleTapReloadRecognizer) + .didDoubleTapR(keyCode, mActivity.getCurrentFocus()); + if (didDoubleTapR) { + devSupportManager.handleReloadJS(); + return true; } return false; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt index 966bbb54fda32e..b66775c2e76ca8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt @@ -9,6 +9,7 @@ package com.facebook.react import android.app.Activity import android.content.Context +import android.content.Intent import android.os.Bundle import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.queue.ReactQueueConfiguration @@ -111,6 +112,22 @@ public interface ReactHost { */ public fun destroy(reason: String, ex: Exception?): TaskInterface + /* To be called when the host activity receives an activity result. */ + public fun onActivityResult( + activity: Activity, + requestCode: Int, + resultCode: Int, + data: Intent?, + ) + + /* To be called when focus has changed for the hosting window. */ + public fun onWindowFocusChange(hasFocus: Boolean) + + /* This method will give JS the opportunity to receive intents via Linking. */ + public fun onNewIntent(intent: Intent) + + public fun onConfigurationChanged(context: Context) + public fun addBeforeDestroyListener(onBeforeDestroy: () -> Unit) public fun removeBeforeDestroyListener(onBeforeDestroy: () -> Unit) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index a600833e264a4a..7258ab91f4dbc1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -998,6 +998,11 @@ public Collection getViewManagerNames() { if (names != null) { uniqueNames.addAll(names); } + } else { + FLog.w( + ReactConstants.TAG, + "Package %s is not a ViewManagerOnDemandReactPackage, view managers will not be loaded", + reactPackage.getClass().getSimpleName()); } Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java index aba8502f55c405..5de97f4ceffc53 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java @@ -18,9 +18,9 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.internal.turbomodule.core.TurboModuleManagerDelegate; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -123,14 +123,14 @@ private void initialize( reactModule.canOverrideExistingModule(), true, reactModule.isCxxModule(), - TurboModule.class.isAssignableFrom(moduleClass)) + ReactModuleInfo.classIsTurboModule(moduleClass)) : new ReactModuleInfo( moduleName, moduleClass.getName(), module.canOverrideExistingModule(), true, CxxModuleWrapper.class.isAssignableFrom(moduleClass), - TurboModule.class.isAssignableFrom(moduleClass)); + ReactModuleInfo.classIsTurboModule(moduleClass)); reactModuleInfoMap.put(moduleName, moduleInfo); moduleMap.put(moduleName, module); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java index 0ee0dd6df275ee..b2e2d7cadc1ff1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -120,7 +120,7 @@ public interface CatalystInstance RuntimeScheduler getRuntimeScheduler(); @Deprecated - void addJSIModules(List jsiModules); + void addJSIModules(List> jsiModules); /** * Returns a hybrid object that contains a pointer to a JS CallInvoker, which is used to schedule diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java index 3658f68cac44f7..f86dffbf341dd2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java @@ -537,7 +537,8 @@ public JavaScriptContextHolder getJavaScriptContextHolder() { public native RuntimeScheduler getRuntimeScheduler(); @Override - public void addJSIModules(List jsiModules) { + @Deprecated + public void addJSIModules(List> jsiModules) { mJSIModuleRegistry.registerModules(jsiModules); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java index e81b361d16841d..90d5cde4fcbce5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java @@ -26,8 +26,8 @@ public JSIModule getModule(JSIModuleType moduleType) { return Assertions.assertNotNull(jsiModuleHolder.getJSIModule()); } - public void registerModules(List jsiModules) { - for (JSIModuleSpec spec : jsiModules) { + public void registerModules(List> jsiModules) { + for (JSIModuleSpec spec : jsiModules) { mModules.put(spec.getJSIModuleType(), new JSIModuleHolder(spec)); } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java index 5e08e165801c6a..44abd25f26a761 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java @@ -15,7 +15,7 @@ import androidx.annotation.Nullable; import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; import java.lang.reflect.Method; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java index 25f170f1134a92..77d011b93cfaa9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java @@ -20,7 +20,6 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.common.ReactConstants; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.model.ReactModuleInfo; import com.facebook.systrace.SystraceMessage; import java.util.concurrent.atomic.AtomicInteger; @@ -73,7 +72,7 @@ public ModuleHolder(NativeModule nativeModule) { nativeModule.canOverrideExistingModule(), true, CxxModuleWrapper.class.isAssignableFrom(nativeModule.getClass()), - TurboModule.class.isAssignableFrom(nativeModule.getClass())); + ReactModuleInfo.classIsTurboModule(nativeModule.getClass())); mModule = nativeModule; PrinterHolder.getPrinter() diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArrayInterface.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArrayInterface.java index a410a3aeb289b3..6791df05e6d510 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArrayInterface.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArrayInterface.java @@ -7,7 +7,7 @@ package com.facebook.react.bridge; -interface NativeArrayInterface { +public interface NativeArrayInterface { @Override String toString(); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java index ddac0bfec7360f..010b6871593ea1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java @@ -10,8 +10,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import com.facebook.common.logging.FLog; -import com.facebook.react.common.ReactConstants; /** Handler that can catch and dispatch Exceptions to an Exception handler. */ public class MessageQueueThreadHandler extends Handler { @@ -28,15 +26,6 @@ public void dispatchMessage(Message msg) { try { super.dispatchMessage(msg); } catch (Exception e) { - if (e instanceof NullPointerException) { - FLog.e( - ReactConstants.TAG, - "Caught NullPointerException when dispatching message in MessageQueueThreadHandler. This is likely caused by runnable" - + "(msg.callback) being nulled in Android Handler after dispatching and before handling (see T170239922 for more details)." - + "Currently we observe that it only happen once which is during initialisation. Due to fixing probably involve Android " - + "System code, we decide to ignore here for now and print an error message for debugging purpose in case this cause more serious issues in future."); - return; - } mExceptionHandler.handleException(e); } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultBindingsInstaller.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultBindingsInstaller.kt deleted file mode 100644 index 06a47d80908ebb..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultBindingsInstaller.kt +++ /dev/null @@ -1,20 +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. - */ - -package com.facebook.react.defaults - -import com.facebook.jni.annotations.DoNotStrip -import com.facebook.react.common.annotations.UnstableReactNativeAPI -import com.facebook.react.runtime.BindingsInstaller - -/** - * A utility class that provides users a default [BindingsInstaller] class that's used to initialize - * [ReactHostDelegate] - */ -@DoNotStrip -@UnstableReactNativeAPI -public class DefaultBindingsInstaller : BindingsInstaller(null) {} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt index 59ffe9bccb7875..0e64a42fd753cc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt @@ -11,6 +11,8 @@ package com.facebook.react.defaults import com.facebook.react.common.annotations.VisibleForTesting import com.facebook.react.config.ReactFeatureFlags +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsDefaults /** * A utility class that serves as an entry point for users setup the New Architecture. @@ -45,6 +47,19 @@ public object DefaultNewArchitectureEntryPoint { ReactFeatureFlags.unstable_useTurboModuleInterop = bridgelessEnabled ReactFeatureFlags.enableFabricPendingEventQueue = fabricEnabled + if (bridgelessEnabled) { + ReactNativeFeatureFlags.override( + object : ReactNativeFeatureFlagsDefaults() { + override fun useModernRuntimeScheduler(): Boolean = true + + override fun enableMicrotasks(): Boolean = true + + override fun batchRenderingUpdatesInEventLoop(): Boolean = true + + override fun androidEnablePendingFabricTransactions(): Boolean = true + }) + } + privateFabricEnabled = fabricEnabled privateTurboModulesEnabled = turboModulesEnabled privateConcurrentReactEnabled = fabricEnabled diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHostDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHostDelegate.kt index 5f53398208a74d..3c50cac823e6f7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHostDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHostDelegate.kt @@ -43,7 +43,7 @@ public class DefaultReactHostDelegate( override val jsBundleLoader: JSBundleLoader, override val reactPackages: List = emptyList(), override val jsRuntimeFactory: JSRuntimeFactory = HermesInstance(), - override val bindingsInstaller: BindingsInstaller = DefaultBindingsInstaller(), + override val bindingsInstaller: BindingsInstaller? = null, private val reactNativeConfig: ReactNativeConfig = ReactNativeConfig.DEFAULT_CONFIG, private val exceptionHandler: (Exception) -> Unit = {}, override val turboModuleManagerDelegateBuilder: ReactPackageTurboModuleManagerDelegate.Builder diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt index da82f02ea68262..817e953d0de25f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt @@ -49,13 +49,18 @@ protected constructor( DefaultComponentsRegistry.register(componentFactory) val viewManagerRegistry = - ViewManagerRegistry( - object : ViewManagerResolver { - override fun getViewManager(viewManagerName: String) = - reactInstanceManager.createViewManager(viewManagerName) + if (lazyViewManagersEnabled) { + ViewManagerRegistry( + object : ViewManagerResolver { + override fun getViewManager(viewManagerName: String) = + reactInstanceManager.createViewManager(viewManagerName) - override fun getViewManagerNames() = reactInstanceManager.viewManagerNames - }) + override fun getViewManagerNames() = reactInstanceManager.viewManagerNames + }) + } else { + ViewManagerRegistry( + reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)) + } FabricUIManagerProviderImpl( componentFactory, ReactNativeConfig.DEFAULT_CONFIG, viewManagerRegistry) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index f091faaf87c6fd..bd76cd15dbd6b1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -57,6 +57,7 @@ import com.facebook.react.fabric.events.FabricEventEmitter; import com.facebook.react.fabric.internal.interop.InteropUIBlockListener; import com.facebook.react.fabric.interop.UIBlock; +import com.facebook.react.fabric.interop.UIBlockViewResolver; import com.facebook.react.fabric.mounting.MountItemDispatcher; import com.facebook.react.fabric.mounting.MountingManager; import com.facebook.react.fabric.mounting.SurfaceMountingManager; @@ -99,7 +100,7 @@ */ @SuppressLint("MissingNativeLoadLibrary") @DoNotStripAny -public class FabricUIManager implements UIManager, LifecycleEventListener { +public class FabricUIManager implements UIManager, LifecycleEventListener, UIBlockViewResolver { public static final String TAG = FabricUIManager.class.getSimpleName(); // The IS_DEVELOPMENT_ENVIRONMENT variable is used to log extra data when running fabric in a diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListener.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListener.kt index 1db876d5aa1b47..3c3f7cfd2d4ff6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListener.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListener.kt @@ -37,6 +37,9 @@ internal class InteropUIBlockListener : UIManagerListener { } override fun willMountItems(uiManager: UIManager) { + if (beforeUIBlocks.isEmpty()) { + return + } beforeUIBlocks.forEach { if (uiManager is UIBlockViewResolver) { it.execute(uiManager) @@ -46,6 +49,9 @@ internal class InteropUIBlockListener : UIManagerListener { } override fun didMountItems(uiManager: UIManager) { + if (afterUIBlocks.isEmpty()) { + return + } afterUIBlocks.forEach { if (uiManager is UIBlockViewResolver) { it.execute(uiManager) @@ -54,9 +60,9 @@ internal class InteropUIBlockListener : UIManagerListener { afterUIBlocks.clear() } - override fun willDispatchViewUpdates(uiManager: UIManager) = Unit + override fun didDispatchMountItems(uiManager: UIManager) = didMountItems(uiManager) - override fun didDispatchMountItems(uiManager: UIManager) = Unit + override fun willDispatchViewUpdates(uiManager: UIManager) = willMountItems(uiManager) override fun didScheduleMountItems(uiManager: UIManager) = Unit } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 487f43d409c9a4..c5f9dc4e0be2e0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -34,6 +34,24 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun commonTestFlag(): Boolean = accessor.commonTestFlag() + /** + * To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically + */ + @JvmStatic + public fun androidEnablePendingFabricTransactions(): Boolean = accessor.androidEnablePendingFabricTransactions() + + /** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ + @JvmStatic + public fun batchRenderingUpdatesInEventLoop(): Boolean = accessor.batchRenderingUpdatesInEventLoop() + + /** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ + @JvmStatic + public fun destroyFabricSurfacesInReactInstanceManager(): Boolean = accessor.destroyFabricSurfacesInReactInstanceManager() + /** * Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used). */ @@ -52,12 +70,6 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableMicrotasks(): Boolean = accessor.enableMicrotasks() - /** - * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. - */ - @JvmStatic - public fun batchRenderingUpdatesInEventLoop(): Boolean = accessor.batchRenderingUpdatesInEventLoop() - /** * Uses new, deduplicated logic for constructing Android Spannables from text fragments */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 3e38e9f251cbd8..512b43d8d92752 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<11824621ee7ca5dbdf2f09bdf1a1f983>> + * @generated SignedSource<> */ /** @@ -21,10 +21,12 @@ package com.facebook.react.internal.featureflags public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccessor { private var commonTestFlagCache: Boolean? = null + private var androidEnablePendingFabricTransactionsCache: Boolean? = null + private var batchRenderingUpdatesInEventLoopCache: Boolean? = null + private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null private var enableBackgroundExecutorCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var enableMicrotasksCache: Boolean? = null - private var batchRenderingUpdatesInEventLoopCache: Boolean? = null private var enableSpannableBuildingUnificationCache: Boolean? = null private var enableCustomDrawOrderFabricCache: Boolean? = null private var enableFixForClippedSubviewsCrashCache: Boolean? = null @@ -40,6 +42,33 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun androidEnablePendingFabricTransactions(): Boolean { + var cached = androidEnablePendingFabricTransactionsCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.androidEnablePendingFabricTransactions() + androidEnablePendingFabricTransactionsCache = cached + } + return cached + } + + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.batchRenderingUpdatesInEventLoop() + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean { + var cached = destroyFabricSurfacesInReactInstanceManagerCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.destroyFabricSurfacesInReactInstanceManager() + destroyFabricSurfacesInReactInstanceManagerCache = cached + } + return cached + } + override fun enableBackgroundExecutor(): Boolean { var cached = enableBackgroundExecutorCache if (cached == null) { @@ -67,15 +96,6 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } - override fun batchRenderingUpdatesInEventLoop(): Boolean { - var cached = batchRenderingUpdatesInEventLoopCache - if (cached == null) { - cached = ReactNativeFeatureFlagsCxxInterop.batchRenderingUpdatesInEventLoop() - batchRenderingUpdatesInEventLoopCache = cached - } - return cached - } - override fun enableSpannableBuildingUnification(): Boolean { var cached = enableSpannableBuildingUnificationCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index d3518e6cba082d..7ce102e95267eb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5fece1bcbbd750b82b8e4b125c57101e>> */ /** @@ -30,14 +30,18 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun commonTestFlag(): Boolean + @DoNotStrip @JvmStatic public external fun androidEnablePendingFabricTransactions(): Boolean + + @DoNotStrip @JvmStatic public external fun batchRenderingUpdatesInEventLoop(): Boolean + + @DoNotStrip @JvmStatic public external fun destroyFabricSurfacesInReactInstanceManager(): Boolean + @DoNotStrip @JvmStatic public external fun enableBackgroundExecutor(): Boolean @DoNotStrip @JvmStatic public external fun useModernRuntimeScheduler(): Boolean @DoNotStrip @JvmStatic public external fun enableMicrotasks(): Boolean - @DoNotStrip @JvmStatic public external fun batchRenderingUpdatesInEventLoop(): Boolean - @DoNotStrip @JvmStatic public external fun enableSpannableBuildingUnification(): Boolean @DoNotStrip @JvmStatic public external fun enableCustomDrawOrderFabric(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index e75ba65f44d08b..96bccc3a818b51 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<4462edcf10a85654be7c71a7438a2288>> */ /** @@ -25,14 +25,18 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun commonTestFlag(): Boolean = false + override fun androidEnablePendingFabricTransactions(): Boolean = false + + override fun batchRenderingUpdatesInEventLoop(): Boolean = false + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean = false + override fun enableBackgroundExecutor(): Boolean = false override fun useModernRuntimeScheduler(): Boolean = false override fun enableMicrotasks(): Boolean = false - override fun batchRenderingUpdatesInEventLoop(): Boolean = false - override fun enableSpannableBuildingUnification(): Boolean = false override fun enableCustomDrawOrderFabric(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 2fcc689a55e16c..acb8e387693b95 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<63356ad414e641eae11ca07b1a876fd3>> + * @generated SignedSource<> */ /** @@ -25,10 +25,12 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private val accessedFeatureFlags = mutableSetOf() private var commonTestFlagCache: Boolean? = null + private var androidEnablePendingFabricTransactionsCache: Boolean? = null + private var batchRenderingUpdatesInEventLoopCache: Boolean? = null + private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null private var enableBackgroundExecutorCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var enableMicrotasksCache: Boolean? = null - private var batchRenderingUpdatesInEventLoopCache: Boolean? = null private var enableSpannableBuildingUnificationCache: Boolean? = null private var enableCustomDrawOrderFabricCache: Boolean? = null private var enableFixForClippedSubviewsCrashCache: Boolean? = null @@ -45,6 +47,36 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun androidEnablePendingFabricTransactions(): Boolean { + var cached = androidEnablePendingFabricTransactionsCache + if (cached == null) { + cached = currentProvider.androidEnablePendingFabricTransactions() + accessedFeatureFlags.add("androidEnablePendingFabricTransactions") + androidEnablePendingFabricTransactionsCache = cached + } + return cached + } + + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = currentProvider.batchRenderingUpdatesInEventLoop() + accessedFeatureFlags.add("batchRenderingUpdatesInEventLoop") + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean { + var cached = destroyFabricSurfacesInReactInstanceManagerCache + if (cached == null) { + cached = currentProvider.destroyFabricSurfacesInReactInstanceManager() + accessedFeatureFlags.add("destroyFabricSurfacesInReactInstanceManager") + destroyFabricSurfacesInReactInstanceManagerCache = cached + } + return cached + } + override fun enableBackgroundExecutor(): Boolean { var cached = enableBackgroundExecutorCache if (cached == null) { @@ -75,16 +107,6 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } - override fun batchRenderingUpdatesInEventLoop(): Boolean { - var cached = batchRenderingUpdatesInEventLoopCache - if (cached == null) { - cached = currentProvider.batchRenderingUpdatesInEventLoop() - accessedFeatureFlags.add("batchRenderingUpdatesInEventLoop") - batchRenderingUpdatesInEventLoopCache = cached - } - return cached - } - override fun enableSpannableBuildingUnification(): Boolean { var cached = enableSpannableBuildingUnificationCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 2d4561cc3e6420..8db8c67bfd2e9a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9190129a45a19b25dd23126f05b03356>> */ /** @@ -25,14 +25,18 @@ import com.facebook.proguard.annotations.DoNotStrip public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun commonTestFlag(): Boolean + @DoNotStrip public fun androidEnablePendingFabricTransactions(): Boolean + + @DoNotStrip public fun batchRenderingUpdatesInEventLoop(): Boolean + + @DoNotStrip public fun destroyFabricSurfacesInReactInstanceManager(): Boolean + @DoNotStrip public fun enableBackgroundExecutor(): Boolean @DoNotStrip public fun useModernRuntimeScheduler(): Boolean @DoNotStrip public fun enableMicrotasks(): Boolean - @DoNotStrip public fun batchRenderingUpdatesInEventLoop(): Boolean - @DoNotStrip public fun enableSpannableBuildingUnification(): Boolean @DoNotStrip public fun enableCustomDrawOrderFabric(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleInteropUtils.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleInteropUtils.java index 50ce80eb46af0b..496cf39a313ba9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleInteropUtils.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleInteropUtils.java @@ -18,7 +18,7 @@ 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 java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.java index e66dbc3492830b..d627f82b7fd3f8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.java @@ -19,12 +19,12 @@ import com.facebook.react.bridge.ReactNoCrashSoftException; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.RuntimeExecutor; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.internal.turbomodule.core.interfaces.TurboModuleRegistry; import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; import com.facebook.react.turbomodule.core.NativeMethodCallInvokerHolderImpl; import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder; import com.facebook.react.turbomodule.core.interfaces.NativeMethodCallInvokerHolder; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.java index 9fe0fdb630d308..39539cdd6f34d5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.java @@ -12,7 +12,7 @@ import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.NativeModule; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import java.util.ArrayList; import java.util.List; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/interfaces/TurboModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/interfaces/TurboModule.kt deleted file mode 100644 index 0e546d282864a3..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/interfaces/TurboModule.kt +++ /dev/null @@ -1,19 +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. - */ - -package com.facebook.react.internal.turbomodule.core.interfaces -/** All turbo modules should inherit from this interface */ -public interface TurboModule { - /** Initialize the TurboModule. */ - public fun initialize() - - /** - * Called during the turn down process of ReactHost. This method is called before React Native is - * stopped. Override this method to clean up resources used by the TurboModule. - */ - public fun invalidate() -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/model/ReactModuleInfo.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/model/ReactModuleInfo.java index ae4a56bbc3a969..8be508edf80479 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/model/ReactModuleInfo.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/model/ReactModuleInfo.java @@ -7,6 +7,8 @@ package com.facebook.react.module.model; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; + /** * Data holder class holding native module specifications. {@link ReactModuleSpecProcessor} creates * these so Java modules don't have to be instantiated at React Native start up. @@ -80,4 +82,12 @@ public boolean isCxxModule() { public boolean isTurboModule() { return mIsTurboModule; } + + /** + * Checks if the passed class is a TurboModule. Useful to populate the parameter [isTurboModule] + * in the constructor of ReactModuleInfo. + */ + public static boolean classIsTurboModule(Class clazz) { + return TurboModule.class.isAssignableFrom(clazz); + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java index 969ea6ddb09939..4f85ff932503b6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/module/processing/ReactModuleSpecProcessor.java @@ -161,13 +161,13 @@ private CodeBlock getCodeBlockForReactModuleInfos(List nativeModules) builder.addStatement("$T map = new $T()", MAP_TYPE, INSTANTIATED_MAP_TYPE); String turboModuleInterfaceCanonicalName = - "com.facebook.react.internal.turbomodule.core.interfaces.TurboModule"; + "com.facebook.react.turbomodule.core.interfaces.TurboModule"; TypeMirror turboModuleInterface = mElements.getTypeElement(turboModuleInterfaceCanonicalName).asType(); if (turboModuleInterface == null) { throw new RuntimeException( - "com.facebook.react.internal.turbomodule.core.interfaces.TurboModule interface not found."); + "com.facebook.react.turbomodule.core.interfaces.TurboModule interface not found."); } for (String nativeModule : nativeModules) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java index 5f5e1abb8f64d3..be017cdd71463e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java @@ -99,8 +99,11 @@ public void doFrame(long frameTimeNanos) { } // If the JS thread is busy for multiple frames we cancel any other pending runnable. - if (mCurrentIdleCallbackRunnable != null) { - mCurrentIdleCallbackRunnable.cancel(); + // We also capture the idleCallbackRunnable to tentatively fix: + // https://github.com/facebook/react-native/issues/44842 + IdleCallbackRunnable idleCallbackRunnable = mCurrentIdleCallbackRunnable; + if (idleCallbackRunnable != null) { + idleCallbackRunnable.cancel(); } mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java index 34f9fd7121b0c7..3ac33b5d0e58bf 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java @@ -19,12 +19,12 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.common.ReactConstants; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.common.ModuleDataCleaner; import com.facebook.react.modules.network.CookieJarContainer; import com.facebook.react.modules.network.ForwardingCookieHandler; import com.facebook.react.modules.network.OkHttpClientProvider; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import java.util.HashSet; import okhttp3.JavaNetCookieJar; import okhttp3.OkHttpClient; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java index b5b7f82b5cf6b9..3c283271487eda 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java @@ -18,8 +18,8 @@ import com.facebook.fbreact.specs.NativePlatformConstantsAndroidSpec; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.common.build.ReactBuildConfig; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import java.util.HashMap; import java.util.Map; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java index 8f5ef045c9c9eb..21967fa4b098eb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java @@ -15,8 +15,8 @@ public class ReactNativeVersion { public static final Map VERSION = MapBuilder.of( - "major", 1000, - "minor", 0, - "patch", 0, + "major", 0, + "minor", 74, + "patch", 2, "prerelease", null); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt new file mode 100644 index 00000000000000..03a18c6c8dae01 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt @@ -0,0 +1,194 @@ +/* + * 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.runtime + +import android.content.res.AssetManager +import com.facebook.proguard.annotations.DoNotStrip +import com.facebook.react.bridge.CatalystInstance +import com.facebook.react.bridge.JSIModule +import com.facebook.react.bridge.JSIModuleSpec +import com.facebook.react.bridge.JSIModuleType +import com.facebook.react.bridge.JavaScriptContextHolder +import com.facebook.react.bridge.JavaScriptModule +import com.facebook.react.bridge.NativeArray +import com.facebook.react.bridge.NativeArrayInterface +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.NativeModuleRegistry +import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener +import com.facebook.react.bridge.RuntimeExecutor +import com.facebook.react.bridge.RuntimeScheduler +import com.facebook.react.bridge.UIManager +import com.facebook.react.bridge.queue.ReactQueueConfiguration +import com.facebook.react.common.annotations.DeprecatedInNewArchitecture +import com.facebook.react.common.annotations.VisibleForTesting +import com.facebook.react.internal.turbomodule.core.interfaces.TurboModuleRegistry +import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder +import com.facebook.react.turbomodule.core.interfaces.NativeMethodCallInvokerHolder + +@DoNotStrip +@DeprecatedInNewArchitecture +public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : CatalystInstance { + + override fun handleMemoryPressure(level: Int) { + throw UnsupportedOperationException("Unimplemented method 'handleMemoryPressure'") + } + + override fun loadScriptFromAssets( + assetManager: AssetManager, + assetURL: String, + loadSynchronously: Boolean + ) { + throw UnsupportedOperationException("Unimplemented method 'loadScriptFromAssets'") + } + + override fun loadScriptFromFile(fileName: String, sourceURL: String, loadSynchronously: Boolean) { + throw UnsupportedOperationException("Unimplemented method 'loadScriptFromFile'") + } + + override fun loadSplitBundleFromFile(fileName: String, sourceURL: String) { + throw UnsupportedOperationException("Unimplemented method 'loadSplitBundleFromFile'") + } + + override fun setSourceURLs(deviceURL: String, remoteURL: String) { + throw UnsupportedOperationException("Unimplemented method 'setSourceURLs'") + } + + override fun runJSBundle() { + throw UnsupportedOperationException("Unimplemented method 'runJSBundle'") + } + + override fun hasRunJSBundle(): Boolean { + throw UnsupportedOperationException("Unimplemented method 'hasRunJSBundle'") + } + + override fun getSourceURL(): String? { + throw UnsupportedOperationException("Unimplemented method 'getSourceURL'") + } + + @DoNotStrip + override fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface) { + throw UnsupportedOperationException("Unimplemented method 'invokeCallback'") + } + + override fun callFunction(module: String, method: String, arguments: NativeArray) { + throw UnsupportedOperationException("Unimplemented method 'callFunction'") + } + + override fun destroy() { + throw UnsupportedOperationException("Unimplemented method 'destroy'") + } + + override fun isDestroyed(): Boolean { + throw UnsupportedOperationException("Unimplemented method 'isDestroyed'") + } + + @VisibleForTesting + override fun initialize() { + throw UnsupportedOperationException("Unimplemented method 'initialize'") + } + + override fun getReactQueueConfiguration(): ReactQueueConfiguration = + reactHost.reactQueueConfiguration!! + + override fun getJSModule(jsInterface: Class): T = + reactHost.currentReactContext?.getJSModule(jsInterface)!! + + override fun hasNativeModule(nativeModuleInterface: Class): Boolean = + reactHost.hasNativeModule(nativeModuleInterface) + + override fun getNativeModule(nativeModuleInterface: Class): T? = + reactHost.getNativeModule(nativeModuleInterface) + + override fun getNativeModule(moduleName: String): NativeModule? = + reactHost.getNativeModule(moduleName) + + @Deprecated( + message = + "getJSIModule(JSIModuleType moduleType) is deprecated and will be deleted in the future. Please use ReactInstanceEventListener to subscribe for react instance events instead.") + override fun getJSIModule(moduleType: JSIModuleType): JSIModule { + throw UnsupportedOperationException("Unimplemented method 'getJSIModule'") + } + + override fun getNativeModules(): Collection = reactHost.getNativeModules() + + override fun extendNativeModules(modules: NativeModuleRegistry) { + throw UnsupportedOperationException("Unimplemented method 'extendNativeModules'") + } + + override fun addBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) { + throw UnsupportedOperationException("Unimplemented method 'addBridgeIdleDebugListener'") + } + + override fun removeBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) { + throw UnsupportedOperationException("Unimplemented method 'removeBridgeIdleDebugListener'") + } + + override fun registerSegment(segmentId: Int, path: String) { + throw UnsupportedOperationException("Unimplemented method 'registerSegment'") + } + + @VisibleForTesting + override fun setGlobalVariable(propName: String, jsonValue: String) { + throw UnsupportedOperationException("Unimplemented method 'setGlobalVariable'") + } + + @Deprecated(message = "This API is unsupported in the New Architecture.") + override fun getJavaScriptContextHolder(): JavaScriptContextHolder? { + return reactHost.getJavaScriptContextHolder() + } + + override fun getRuntimeExecutor(): RuntimeExecutor? { + return reactHost.getRuntimeExecutor() + } + + override fun getRuntimeScheduler(): RuntimeScheduler { + throw UnsupportedOperationException("Unimplemented method 'getRuntimeScheduler'") + } + + @Deprecated(message = "This API is unsupported in the New Architecture.") + override fun addJSIModules(jsiModules: List>) { + throw UnsupportedOperationException("Unimplemented method 'addJSIModules'") + } + + override fun getJSCallInvokerHolder(): CallInvokerHolder? { + return reactHost.getJSCallInvokerHolder() + } + + override fun getNativeMethodCallInvokerHolder(): NativeMethodCallInvokerHolder { + throw UnsupportedOperationException("Unimplemented method 'getNativeMethodCallInvokerHolder'") + } + + @Deprecated( + message = + "setTurboModuleManager(JSIModule getter) is deprecated and will be deleted in the future. Please use setTurboModuleRegistry(TurboModuleRegistry turboModuleRegistry) instead.", + replaceWith = ReplaceWith("setTurboModuleRegistry(turboModuleRegistry)")) + override fun setTurboModuleManager(getter: JSIModule) { + throw UnsupportedOperationException("Unimplemented method 'setTurboModuleManager'") + } + + @DeprecatedInNewArchitecture( + message = + "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.") + override fun setTurboModuleRegistry(turboModuleRegistry: TurboModuleRegistry) { + throw UnsupportedOperationException("Unimplemented method 'setTurboModuleRegistry'") + } + + @DeprecatedInNewArchitecture( + message = + "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.") + override fun setFabricUIManager(fabricUIManager: UIManager) { + throw UnsupportedOperationException("Unimplemented method 'setFabricUIManager'") + } + + @DeprecatedInNewArchitecture( + message = + "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.") + override fun getFabricUIManager(): UIManager { + throw UnsupportedOperationException("Unimplemented method 'getFabricUIManager'") + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java index 0e424960ddc88e..e71937dd6d7f6f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java @@ -119,7 +119,7 @@ public void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory) @Override public void onJSBundleLoadedFromServer() { - throw new IllegalStateException("Not implemented for bridgeless mode"); + // Not implemented } @Override diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java index 5fa3947959105c..d1cdaa976fc1d5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java @@ -8,6 +8,7 @@ package com.facebook.react.runtime; import android.content.Context; +import android.util.Log; import com.facebook.infer.annotation.Nullsafe; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; @@ -18,8 +19,6 @@ import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactNoCrashBridgeNotAllowedSoftException; -import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.RuntimeExecutor; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.WritableNativeArray; @@ -84,11 +83,10 @@ public void setSourceURL(String sourceURL) { @Override public CatalystInstance getCatalystInstance() { - ReactSoftExceptionLogger.logSoftExceptionVerbose( + Log.w( TAG, - new ReactNoCrashBridgeNotAllowedSoftException( - "getCatalystInstance() cannot be called when the bridge is disabled")); - throw new UnsupportedOperationException("There is no Catalyst instance in bridgeless mode."); + "[WARNING] Bridgeless doesn't support CatalystInstance. Accessing an API that's not part of the new architecture is not encouraged usage."); + return new BridgelessCatalystInstance(mReactHost); } @Override diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/CoreReactPackage.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/CoreReactPackage.java index cb8b9c13af09ae..deae9de23347e3 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/CoreReactPackage.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/CoreReactPackage.java @@ -14,7 +14,6 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.devsupport.LogBoxModule; import com.facebook.react.devsupport.interfaces.DevSupportManager; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModuleList; import com.facebook.react.module.model.ReactModuleInfo; @@ -103,7 +102,7 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { reactModule.canOverrideExistingModule(), reactModule.needsEagerInit(), reactModule.isCxxModule(), - TurboModule.class.isAssignableFrom(moduleClass))); + ReactModuleInfo.classIsTurboModule(moduleClass))); } } return () -> reactModuleInfoMap; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java index 674aeb42f9f7fd..054c36a260e207 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java @@ -15,6 +15,9 @@ import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.nfc.NfcAdapter; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -54,11 +57,13 @@ import com.facebook.react.interfaces.TaskInterface; import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler; import com.facebook.react.interfaces.fabric.ReactSurface; +import com.facebook.react.modules.appearance.AppearanceModule; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.runtime.internal.bolts.Continuation; import com.facebook.react.runtime.internal.bolts.Task; import com.facebook.react.runtime.internal.bolts.TaskCompletionSource; +import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.events.BlackHoleEventDispatcher; import com.facebook.react.uimanager.events.EventDispatcher; @@ -587,6 +592,16 @@ T getNativeModule(Class nativeModuleInterface) { return null; } + /* package */ + @Nullable + NativeModule getNativeModule(String nativeModuleName) { + final ReactInstance reactInstance = mReactInstanceTaskRef.get().getResult(); + if (reactInstance != null) { + return reactInstance.getNativeModule(nativeModuleName); + } + return null; + } + /* package */ @Nullable RuntimeExecutor getRuntimeExecutor() { @@ -600,6 +615,115 @@ RuntimeExecutor getRuntimeExecutor() { return null; } + /* package */ + @Nullable + CallInvokerHolder getJSCallInvokerHolder() { + final ReactInstance reactInstance = mReactInstanceTaskRef.get().getResult(); + if (reactInstance != null) { + return reactInstance.getJSCallInvokerHolder(); + } + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Tried to get JSCallInvokerHolder while instance is not ready")); + return null; + } + + /** + * To be called when the host activity receives an activity result. + * + * @param activity The host activity + */ + @ThreadConfined(UI) + @Override + public void onActivityResult( + Activity activity, int requestCode, int resultCode, @Nullable Intent data) { + final String method = + "onActivityResult(activity = \"" + + activity + + "\", requestCode = \"" + + requestCode + + "\", resultCode = \"" + + resultCode + + "\", data = \"" + + data + + "\")"; + log(method); + + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null) { + currentContext.onActivityResult(activity, requestCode, resultCode, data); + } else { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Tried to access onActivityResult while context is not ready")); + } + } + + /* To be called when focus has changed for the hosting window. */ + @ThreadConfined(UI) + @Override + public void onWindowFocusChange(boolean hasFocus) { + final String method = "onWindowFocusChange(hasFocus = \"" + hasFocus + "\")"; + log(method); + + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null) { + currentContext.onWindowFocusChange(hasFocus); + } else { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Tried to access onWindowFocusChange while context is not ready")); + } + } + + /* This method will give JS the opportunity to receive intents via Linking. + * + * @param intent The incoming intent + */ + @ThreadConfined(UI) + @Override + public void onNewIntent(Intent intent) { + log("onNewIntent()"); + + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null) { + String action = intent.getAction(); + Uri uri = intent.getData(); + + if (uri != null + && (Intent.ACTION_VIEW.equals(action) + || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))) { + DeviceEventManagerModule deviceEventManagerModule = + currentContext.getNativeModule(DeviceEventManagerModule.class); + if (deviceEventManagerModule != null) { + deviceEventManagerModule.emitNewIntentReceived(uri); + } + } + currentContext.onNewIntent(getCurrentActivity(), intent); + } else { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException("Tried to access onNewIntent while context is not ready")); + } + } + + @ThreadConfined(UI) + @Override + public void onConfigurationChanged(Context updatedContext) { + ReactContext currentReactContext = getCurrentReactContext(); + if (currentReactContext != null) { + AppearanceModule appearanceModule = + currentReactContext.getNativeModule(AppearanceModule.class); + + if (appearanceModule != null) { + appearanceModule.onConfigurationChanged(updatedContext); + } + } + } + @Nullable JavaScriptContextHolder getJavaScriptContextHolder() { final ReactInstance reactInstance = mReactInstanceTaskRef.get().getResult(); @@ -1402,9 +1526,9 @@ private Task getOrCreateDestroyTask(final String reason, @Nullable Excepti // Step 3: Stop all React Native surfaces stopAttachedSurfaces(method, reactInstance); - - // TODO(T161461674): Should we clear mAttachedSurfaces? - // Not clearing mAttachedSurfaces could lead to a memory leak. + synchronized (mAttachedSurfaces) { + mAttachedSurfaces.clear(); + } return task; }, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java index 37d8d44d830a4b..7f241ce4d7fc58 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java @@ -15,6 +15,7 @@ import com.facebook.infer.annotation.ThreadSafe; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.DebugCorePackage; import com.facebook.react.ReactPackage; import com.facebook.react.ViewManagerOnDemandReactPackage; import com.facebook.react.bridge.Arguments; @@ -195,6 +196,9 @@ public void onHostDestroy() { new CoreReactPackage( bridgelessReactContext.getDevSupportManager(), bridgelessReactContext.getDefaultHardwareBackBtnHandler())); + if (useDevSupport) { + mReactPackages.add(new DebugCorePackage()); + } mReactPackages.addAll(mDelegate.getReactPackages()); TurboModuleManagerDelegate turboModuleManagerDelegate = @@ -475,7 +479,7 @@ private native HybridData initHybrid( private native void loadJSBundleFromAssets(AssetManager assetManager, String assetURL); - private native CallInvokerHolderImpl getJSCallInvokerHolder(); + /* package */ native CallInvokerHolderImpl getJSCallInvokerHolder(); private native NativeMethodCallInvokerHolderImpl getNativeMethodCallInvokerHolder(); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index b06dcc7d745983..b95774889eb4fc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -15,7 +15,6 @@ import com.facebook.react.bridge.ModuleSpec; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModuleList; import com.facebook.react.module.model.ReactModuleInfo; @@ -47,7 +46,6 @@ import com.facebook.react.views.drawer.ReactDrawerLayoutManager; import com.facebook.react.views.image.ReactImageManager; import com.facebook.react.views.modal.ReactModalHostManager; -import com.facebook.react.views.popupmenu.ReactPopupMenuManager; import com.facebook.react.views.progressbar.ReactProgressBarViewManager; import com.facebook.react.views.scroll.ReactHorizontalScrollContainerViewManager; import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager; @@ -171,7 +169,6 @@ public List createViewManagers(ReactApplicationContext reactContext viewManagers.add(new ReactScrollViewManager()); viewManagers.add(new ReactSwitchManager()); viewManagers.add(new SwipeRefreshLayoutManager()); - viewManagers.add(new ReactPopupMenuManager()); // Native equivalents viewManagers.add(new FrescoBasedReactTextInlineImageViewManager()); @@ -213,7 +210,6 @@ public Map getViewManagersMap() { appendMap(viewManagers, ReactSwitchManager.REACT_CLASS, ReactSwitchManager::new); appendMap( viewManagers, SwipeRefreshLayoutManager.REACT_CLASS, SwipeRefreshLayoutManager::new); - appendMap(viewManagers, ReactPopupMenuManager.REACT_CLASS, ReactPopupMenuManager::new); appendMap( viewManagers, FrescoBasedReactTextInlineImageViewManager.REACT_CLASS, @@ -300,7 +296,7 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { reactModule.canOverrideExistingModule(), reactModule.needsEagerInit(), reactModule.isCxxModule(), - TurboModule.class.isAssignableFrom(moduleClass))); + ReactModuleInfo.classIsTurboModule(moduleClass))); } } return () -> reactModuleInfoMap; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.kt index 394ffd69bfa622..6333b8125400b8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.kt @@ -6,15 +6,14 @@ */ package com.facebook.react.turbomodule.core.interfaces +/** All turbo modules should inherit from this interface */ +public interface TurboModule { + /** Initialize the TurboModule. */ + public fun initialize() -import com.facebook.react.common.annotations.DeprecatedInNewArchitecture - -/** - * This interface was introduced for backward compatibility purposes. This interface will be - * deprecated as part of the deprecation and removal of ReactModuleInfoProvider in the near future. - * - * See description of https://github.com/facebook/react-native/pull/41412 for more context. - */ -@DeprecatedInNewArchitecture -public interface TurboModule : - com.facebook.react.internal.turbomodule.core.interfaces.TurboModule {} + /** + * Called during the turn down process of ReactHost. This method is called before React Native is + * stopped. Override this method to clean up resources used by the TurboModule. + */ + public fun invalidate() +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 0dbe9352b779e8..6b3ac1afd410de 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -177,7 +177,7 @@ public void removeRootView(int rootViewTag) { * * @return The num of root view */ - private int getRootViewNum() { + public int getRootViewNum() { return mOperationsQueue.getNativeViewHierarchyManager().getRootViewNum(); } @@ -589,12 +589,6 @@ public void measureLayoutRelativeToParent( /** Invoked at the end of the transaction to commit any updates to the node hierarchy. */ public void dispatchViewUpdates(int batchId) { - if (getRootViewNum() <= 0) { - // If there are no RootViews registered, there will be no View updates to dispatch. - // This is a hack to prevent this from being called when Fabric is used everywhere. - // This should no longer be necessary in Bridgeless Mode. - return; - } SystraceMessage.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "UIImplementation.dispatchViewUpdates") .arg("batchId", batchId) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 15a1b42542989d..dca4fcf5b13098 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -707,7 +707,12 @@ public void onBatchComplete() { listener.willDispatchViewUpdates(this); } try { - mUIImplementation.dispatchViewUpdates(batchId); + // If there are no RootViews registered, there will be no View updates to dispatch. + // This is a hack to prevent this from being called when Fabric is used everywhere. + // This should no longer be necessary in Bridgeless Mode. + if (mUIImplementation.getRootViewNum() > 0) { + mUIImplementation.dispatchViewUpdates(batchId); + } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java index 1f3c219250a71f..b7d7e969649967 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java @@ -53,16 +53,32 @@ public ViewManagerRegistry(Map viewManagerMap) { * @return the {@link ViewManager} registered to the className received as a parameter */ public synchronized ViewManager get(String className) { + // 1. Try to get the manager without the prefix. ViewManager viewManager = mViewManagers.get(className); if (viewManager != null) { return viewManager; } + + // 2. Try to get the manager with the RCT prefix. + String rctViewManagerName = "RCT" + className; + viewManager = mViewManagers.get(rctViewManagerName); + if (viewManager != null) { + return viewManager; + } if (mViewManagerResolver != null) { + // 1. Try to get the manager without the prefix. viewManager = getViewManagerFromResolver(className); if (viewManager != null) return viewManager; + + // 2. Try to get the manager with the RCT prefix. + viewManager = getViewManagerFromResolver(rctViewManagerName); + if (viewManager != null) return viewManager; + throw new IllegalViewOperationException( - "ViewManagerResolver returned null for " + "ViewManagerResolver returned null for either " + className + + " or " + + rctViewManagerName + ", existing names are: " + mViewManagerResolver.getViewManagerNames()); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 7dbcb788c3ac32..82976b53dc0f17 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -807,7 +807,7 @@ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolea // more information. if (!mScroller.isFinished() && mScroller.getCurrX() != mScroller.getFinalX()) { - int scrollRange = computeHorizontalScrollRange() - getWidth(); + int scrollRange = Math.max(computeHorizontalScrollRange() - getWidth(), 0); if (scrollX >= scrollRange) { mScroller.abortAnimation(); scrollX = scrollRange; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java index 25791e00679b34..202734f3795984 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java @@ -62,6 +62,8 @@ public class TextAttributeProps implements EffectiveTextAttributeProvider { public static final short TA_KEY_ROLE = 26; public static final short TA_KEY_TEXT_TRANSFORM = 27; + public static final int UNSET = -1; + private static final String PROP_SHADOW_OFFSET = "textShadowOffset"; private static final String PROP_SHADOW_OFFSET_WIDTH = "width"; private static final String PROP_SHADOW_OFFSET_HEIGHT = "height"; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp index b19acb9108ace0..c3a0c8c226ded1 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp @@ -474,8 +474,7 @@ std::shared_ptr Binding::getMountingManager( void Binding::schedulerDidFinishTransaction( const MountingCoordinator::Shared& mountingCoordinator) { - auto mountingManager = getMountingManager("schedulerDidFinishTransaction"); - if (!mountingManager) { + if (!ReactNativeFeatureFlags::androidEnablePendingFabricTransactions()) { return; } @@ -483,7 +482,43 @@ void Binding::schedulerDidFinishTransaction( if (!mountingTransaction.has_value()) { return; } - mountingManager->executeMount(*mountingTransaction); + + std::unique_lock lock(pendingTransactionsMutex_); + auto pendingTransaction = std::find_if( + pendingTransactions_.begin(), + pendingTransactions_.end(), + [&](const auto& transaction) { + return transaction.getSurfaceId() == + mountingTransaction->getSurfaceId(); + }); + + if (pendingTransaction != pendingTransactions_.end()) { + pendingTransaction->mergeWith(std::move(*mountingTransaction)); + } else { + pendingTransactions_.push_back(std::move(*mountingTransaction)); + } +} + +void Binding::schedulerShouldRenderTransactions( + const MountingCoordinator::Shared& mountingCoordinator) { + auto mountingManager = + getMountingManager("schedulerShouldRenderTransactions"); + if (!mountingManager) { + return; + } + + if (ReactNativeFeatureFlags::androidEnablePendingFabricTransactions()) { + std::unique_lock lock(pendingTransactionsMutex_); + for (auto& transaction : pendingTransactions_) { + mountingManager->executeMount(transaction); + } + pendingTransactions_.clear(); + } else { + auto mountingTransaction = mountingCoordinator->pullTransaction(); + if (mountingTransaction.has_value()) { + mountingManager->executeMount(*mountingTransaction); + } + } } void Binding::schedulerDidRequestPreliminaryViewAllocation( diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h index b310f3bd2962be..dec5855b096fda 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -101,6 +102,9 @@ class Binding : public jni::HybridClass, void schedulerDidFinishTransaction( const MountingCoordinator::Shared& mountingCoordinator) override; + void schedulerShouldRenderTransactions( + const MountingCoordinator::Shared& mountingCoordinator) override; + void schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, const ShadowNode& shadowNode) override; @@ -146,6 +150,10 @@ class Binding : public jni::HybridClass, std::shared_mutex surfaceHandlerRegistryMutex_; // Protects `surfaceHandlerRegistry_`. + // Track pending transactions, one per surfaceId + std::mutex pendingTransactionsMutex_; + std::vector pendingTransactions_; + float pointScaleFactor_ = 1; std::shared_ptr reactNativeConfig_{nullptr}; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp index fdb2f051621fd2..fd8e579865f73b 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp @@ -67,8 +67,6 @@ CoreComponentsRegistry::sharedProviderRegistry() { AndroidDrawerLayoutComponentDescriptor>()); providerRegistry->add(concreteComponentDescriptorProvider< DebuggingOverlayComponentDescriptor>()); - providerRegistry->add(concreteComponentDescriptorProvider< - AndroidPopupMenuComponentDescriptor>()); return providerRegistry; }(); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 15061428a2a2df..d800c08086fc15 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<7ab741aec808bc3a138470fe18ef8b8a>> */ /** @@ -45,6 +45,24 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool androidEnablePendingFabricTransactions() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("androidEnablePendingFabricTransactions"); + return method(javaProvider_); + } + + bool batchRenderingUpdatesInEventLoop() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("batchRenderingUpdatesInEventLoop"); + return method(javaProvider_); + } + + bool destroyFabricSurfacesInReactInstanceManager() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("destroyFabricSurfacesInReactInstanceManager"); + return method(javaProvider_); + } + bool enableBackgroundExecutor() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableBackgroundExecutor"); @@ -63,12 +81,6 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } - bool batchRenderingUpdatesInEventLoop() override { - static const auto method = - getReactNativeFeatureFlagsProviderJavaClass()->getMethod("batchRenderingUpdatesInEventLoop"); - return method(javaProvider_); - } - bool enableSpannableBuildingUnification() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableSpannableBuildingUnification"); @@ -108,6 +120,21 @@ bool JReactNativeFeatureFlagsCxxInterop::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool JReactNativeFeatureFlagsCxxInterop::androidEnablePendingFabricTransactions( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::androidEnablePendingFabricTransactions(); +} + +bool JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + +bool JReactNativeFeatureFlagsCxxInterop::destroyFabricSurfacesInReactInstanceManager( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableBackgroundExecutor( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableBackgroundExecutor(); @@ -123,11 +150,6 @@ bool JReactNativeFeatureFlagsCxxInterop::enableMicrotasks( return ReactNativeFeatureFlags::enableMicrotasks(); } -bool JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop( - facebook::jni::alias_ref /*unused*/) { - return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); -} - bool JReactNativeFeatureFlagsCxxInterop::enableSpannableBuildingUnification( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableSpannableBuildingUnification(); @@ -173,6 +195,15 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "commonTestFlag", JReactNativeFeatureFlagsCxxInterop::commonTestFlag), + makeNativeMethod( + "androidEnablePendingFabricTransactions", + JReactNativeFeatureFlagsCxxInterop::androidEnablePendingFabricTransactions), + makeNativeMethod( + "batchRenderingUpdatesInEventLoop", + JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop), + makeNativeMethod( + "destroyFabricSurfacesInReactInstanceManager", + JReactNativeFeatureFlagsCxxInterop::destroyFabricSurfacesInReactInstanceManager), makeNativeMethod( "enableBackgroundExecutor", JReactNativeFeatureFlagsCxxInterop::enableBackgroundExecutor), @@ -182,9 +213,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableMicrotasks", JReactNativeFeatureFlagsCxxInterop::enableMicrotasks), - makeNativeMethod( - "batchRenderingUpdatesInEventLoop", - JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop), makeNativeMethod( "enableSpannableBuildingUnification", JReactNativeFeatureFlagsCxxInterop::enableSpannableBuildingUnification), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index ff7881e09e417c..ce97635d8fadbc 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<0d37cc1fb8217b0f4a849e178ec2ec68>> */ /** @@ -33,6 +33,15 @@ class JReactNativeFeatureFlagsCxxInterop static bool commonTestFlag( facebook::jni::alias_ref); + static bool androidEnablePendingFabricTransactions( + facebook::jni::alias_ref); + + static bool batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref); + + static bool destroyFabricSurfacesInReactInstanceManager( + facebook::jni::alias_ref); + static bool enableBackgroundExecutor( facebook::jni::alias_ref); @@ -42,9 +51,6 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableMicrotasks( facebook::jni::alias_ref); - static bool batchRenderingUpdatesInEventLoop( - facebook::jni::alias_ref); - static bool enableSpannableBuildingUnification( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.cpp index df01e447a42e5d..e945c01e267944 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.h b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.h index 3926fa906deaaf..716e58b0a7b61e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JMessageQueueThread.h @@ -42,10 +42,6 @@ class JMessageQueueThread : public MessageQueueThread { */ void quitSynchronous() override; - JavaMessageQueueThread::javaobject jobj() { - return m_jobj.get(); - } - private: jni::global_ref m_jobj; }; diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt index 85ae9a6d476476..49e9709df4a1e4 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt @@ -7,7 +7,7 @@ package com.facebook.react.bridge -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModule +import com.facebook.react.turbomodule.core.interfaces.TurboModule import com.facebook.testutils.shadows.ShadowSoLoader import org.junit.Before import org.junit.Test diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListenerTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListenerTest.kt index d875aa83ef8699..71985256665153 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListenerTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/internal/interop/InteropUiBlockListenerTest.kt @@ -43,6 +43,18 @@ class InteropUiBlockListenerTest { assertEquals(1, underTest.afterUIBlocks.size) } + @Test + fun willDispatchViewUpdates_emptiesBeforeUIBlocks() { + val underTest = InteropUIBlockListener() + underTest.prependUIBlock {} + underTest.addUIBlock {} + + underTest.willDispatchViewUpdates(FakeUIManager()) + + assertEquals(0, underTest.beforeUIBlocks.size) + assertEquals(1, underTest.afterUIBlocks.size) + } + @Test fun didMountItems_emptiesAfterUIBlocks() { val underTest = InteropUIBlockListener() @@ -55,6 +67,18 @@ class InteropUiBlockListenerTest { assertEquals(0, underTest.afterUIBlocks.size) } + @Test + fun didDispatchMountItems_emptiesAfterUIBlocks() { + val underTest = InteropUIBlockListener() + underTest.prependUIBlock {} + underTest.addUIBlock {} + + underTest.didDispatchMountItems(FakeUIManager()) + + assertEquals(1, underTest.beforeUIBlocks.size) + assertEquals(0, underTest.afterUIBlocks.size) + } + @Test fun willMountItems_deliversUiManagerCorrectly() { val fakeUIManager = FakeUIManager() @@ -67,6 +91,18 @@ class InteropUiBlockListenerTest { assertEquals(1, fakeUIManager.resolvedViewCount) } + @Test + fun willDispatchViewUpdates_deliversUiManagerCorrectly() { + val fakeUIManager = FakeUIManager() + val underTest = InteropUIBlockListener() + + underTest.prependUIBlock { uiManager -> uiManager.resolveView(0) } + + underTest.willDispatchViewUpdates(fakeUIManager) + + assertEquals(1, fakeUIManager.resolvedViewCount) + } + @Test fun didMountItems_deliversUiManagerCorrectly() { val fakeUIManager = FakeUIManager() @@ -78,4 +114,16 @@ class InteropUiBlockListenerTest { assertEquals(1, fakeUIManager.resolvedViewCount) } + + @Test + fun didDispatchMountItems_deliversUiManagerCorrectly() { + val fakeUIManager = FakeUIManager() + val underTest = InteropUIBlockListener() + + underTest.addUIBlock { uiManager -> uiManager.resolveView(0) } + + underTest.didDispatchMountItems(fakeUIManager) + + assertEquals(1, fakeUIManager.resolvedViewCount) + } } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/model/ReactModuleInfoTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/model/ReactModuleInfoTest.kt new file mode 100644 index 00000000000000..d3c072a6e5d10b --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/model/ReactModuleInfoTest.kt @@ -0,0 +1,51 @@ +/* + * 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.modules.model + +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.turbomodule.core.interfaces.TurboModule +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class ReactModuleInfoTest { + + @Test + fun testCreateReactModuleInfo() { + val reactModuleInfo = + ReactModuleInfo( + /* name = */ "name", + /* className = */ "class", + /* canOverrideExistingModule = */ false, + /* needsEagerInit = */ false, + /* isCxxModule = */ false, + /* isTurboModule = */ false) + assertEquals("name", reactModuleInfo.name()) + assertFalse(reactModuleInfo.canOverrideExistingModule()) + assertFalse(reactModuleInfo.needsEagerInit()) + assertFalse(reactModuleInfo.isCxxModule) + assertFalse(reactModuleInfo.isTurboModule) + } + + @Test + fun classIsTurboModule_withRandomClass() { + assertFalse(ReactModuleInfo.classIsTurboModule(String::class.java)) + } + + @Test + fun classIsTurboModule_withTurboModule() { + assertTrue(ReactModuleInfo.classIsTurboModule(TestTurboModule::class.java)) + } + + inner class TestTurboModule : TurboModule { + override fun initialize() = Unit + + override fun invalidate() = Unit + } +} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/runtime/BridgelessReactContextTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/runtime/BridgelessReactContextTest.kt index d8a8c3b473091d..a3bf2053394eda 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/runtime/BridgelessReactContextTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/runtime/BridgelessReactContextTest.kt @@ -55,9 +55,9 @@ class BridgelessReactContextTest { Assertions.assertThat(bridgelessReactContext.getFabricUIManager()).isEqualTo(fabricUiManager) } - @Test(expected = UnsupportedOperationException::class) - fun getCatalystInstance_throwsException() { - // Disable this test for now due to mocking FabricUIManager fails - bridgelessReactContext.catalystInstance + @Test + fun getCatalystInstanceTest() { + Assertions.assertThat(bridgelessReactContext.getCatalystInstance()) + .isInstanceOf(BridgelessCatalystInstance::class.java) } } diff --git a/packages/react-native/ReactCommon/ReactCommon.podspec b/packages/react-native/ReactCommon/ReactCommon.podspec index 0b6f175895de6e..599e3f97a1c6c7 100644 --- a/packages/react-native/ReactCommon/ReactCommon.podspec +++ b/packages/react-native/ReactCommon/ReactCommon.podspec @@ -36,6 +36,7 @@ Pod::Spec.new do |s| s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fmt/include\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", "USE_HEADERMAP" => "YES", + "DEFINES_MODULE" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", "GCC_WARN_PEDANTIC" => "YES" } if ENV['USE_FRAMEWORKS'] diff --git a/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h b/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h index 6be46f63b85abd..e5a00f22912621 100644 --- a/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h +++ b/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h @@ -15,9 +15,9 @@ namespace facebook::react { constexpr struct { - int32_t Major = 1000; - int32_t Minor = 0; - int32_t Patch = 0; + int32_t Major = 0; + int32_t Minor = 74; + int32_t Patch = 2; std::string_view Prerelease = ""; } ReactNativeVersion; diff --git a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp index c42d4f3008e4a6..235028a2b355a4 100644 --- a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp +++ b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp @@ -370,11 +370,13 @@ JSCRuntime::JSCRuntime(JSGlobalContextRef ctx) { #ifndef NDEBUG #ifdef _JSC_HAS_INSPECTABLE +#if (__OSX_AVAILABLE_STARTING(MAC_NA, IPHONE_16_4)) if (__builtin_available(macOS 13.3, iOS 16.4, tvOS 16.4, *)) { JSGlobalContextSetInspectable(ctx_, true); } #endif #endif +#endif } JSCRuntime::~JSCRuntime() { diff --git a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec index 3def87251c53b0..0202b89061497e 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec +++ b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec @@ -41,7 +41,7 @@ Pod::Spec.new do |s| s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags if ENV['USE_FRAMEWORKS'] - s.header_mappings_dir = './' + s.header_mappings_dir = '../' s.module_name = 'React_jserrorhandler' end diff --git a/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec b/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec index e338fcd843e9b9..0a88f2362f477d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec +++ b/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec @@ -38,7 +38,8 @@ Pod::Spec.new do |s| s.compiler_flags = folly_compiler_flags s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/..\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fmt/include\"", - "CLANG_CXX_LANGUAGE_STANDARD" => "c++20" + "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", + "DEFINES_MODULE" => "YES" }.merge!(use_frameworks ? { "PUBLIC_HEADERS_FOLDER_PATH" => "#{module_name}.framework/Headers/#{header_dir}" } : {}) diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index caec5678dd7b3c..3e4989ac3482bb 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<82f226df2b3824d03b755a042b20bec5>> + * @generated SignedSource<<7ba277e9902ca2a0b171daf223644b89>> */ /** @@ -25,6 +25,18 @@ bool ReactNativeFeatureFlags::commonTestFlag() { return getAccessor().commonTestFlag(); } +bool ReactNativeFeatureFlags::androidEnablePendingFabricTransactions() { + return getAccessor().androidEnablePendingFabricTransactions(); +} + +bool ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop() { + return getAccessor().batchRenderingUpdatesInEventLoop(); +} + +bool ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager() { + return getAccessor().destroyFabricSurfacesInReactInstanceManager(); +} + bool ReactNativeFeatureFlags::enableBackgroundExecutor() { return getAccessor().enableBackgroundExecutor(); } @@ -37,10 +49,6 @@ bool ReactNativeFeatureFlags::enableMicrotasks() { return getAccessor().enableMicrotasks(); } -bool ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop() { - return getAccessor().batchRenderingUpdatesInEventLoop(); -} - bool ReactNativeFeatureFlags::enableSpannableBuildingUnification() { return getAccessor().enableSpannableBuildingUnification(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 8f82ea192014c9..2dbc253f97f6fa 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3bfae310dfd28040f4b80d1a1df8b7b3>> + * @generated SignedSource<> */ /** @@ -38,6 +38,21 @@ class ReactNativeFeatureFlags { */ static bool commonTestFlag(); + /** + * To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically + */ + static bool androidEnablePendingFabricTransactions(); + + /** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ + static bool batchRenderingUpdatesInEventLoop(); + + /** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ + static bool destroyFabricSurfacesInReactInstanceManager(); + /** * Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used). */ @@ -53,11 +68,6 @@ class ReactNativeFeatureFlags { */ static bool enableMicrotasks(); - /** - * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. - */ - static bool batchRenderingUpdatesInEventLoop(); - /** * Uses new, deduplicated logic for constructing Android Spannables from text fragments */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index d46d461bfd4261..0bbed9699bdbed 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2154292f89306f25289583537833a065>> + * @generated SignedSource<<5311a007bec258c79bf1ca3e2355101c>> */ /** @@ -47,8 +47,8 @@ bool ReactNativeFeatureFlagsAccessor::commonTestFlag() { return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::enableBackgroundExecutor() { - auto flagValue = enableBackgroundExecutor_.load(); +bool ReactNativeFeatureFlagsAccessor::androidEnablePendingFabricTransactions() { + auto flagValue = androidEnablePendingFabricTransactions_.load(); if (!flagValue.has_value()) { // This block is not exclusive but it is not necessary. @@ -56,17 +56,17 @@ bool ReactNativeFeatureFlagsAccessor::enableBackgroundExecutor() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(1, "enableBackgroundExecutor"); + markFlagAsAccessed(1, "androidEnablePendingFabricTransactions"); - flagValue = currentProvider_->enableBackgroundExecutor(); - enableBackgroundExecutor_ = flagValue; + flagValue = currentProvider_->androidEnablePendingFabricTransactions(); + androidEnablePendingFabricTransactions_ = flagValue; } return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { - auto flagValue = useModernRuntimeScheduler_.load(); +bool ReactNativeFeatureFlagsAccessor::batchRenderingUpdatesInEventLoop() { + auto flagValue = batchRenderingUpdatesInEventLoop_.load(); if (!flagValue.has_value()) { // This block is not exclusive but it is not necessary. @@ -74,17 +74,17 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(2, "useModernRuntimeScheduler"); + markFlagAsAccessed(2, "batchRenderingUpdatesInEventLoop"); - flagValue = currentProvider_->useModernRuntimeScheduler(); - useModernRuntimeScheduler_ = flagValue; + flagValue = currentProvider_->batchRenderingUpdatesInEventLoop(); + batchRenderingUpdatesInEventLoop_ = flagValue; } return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { - auto flagValue = enableMicrotasks_.load(); +bool ReactNativeFeatureFlagsAccessor::destroyFabricSurfacesInReactInstanceManager() { + auto flagValue = destroyFabricSurfacesInReactInstanceManager_.load(); if (!flagValue.has_value()) { // This block is not exclusive but it is not necessary. @@ -92,17 +92,17 @@ bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(3, "enableMicrotasks"); + markFlagAsAccessed(3, "destroyFabricSurfacesInReactInstanceManager"); - flagValue = currentProvider_->enableMicrotasks(); - enableMicrotasks_ = flagValue; + flagValue = currentProvider_->destroyFabricSurfacesInReactInstanceManager(); + destroyFabricSurfacesInReactInstanceManager_ = flagValue; } return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::batchRenderingUpdatesInEventLoop() { - auto flagValue = batchRenderingUpdatesInEventLoop_.load(); +bool ReactNativeFeatureFlagsAccessor::enableBackgroundExecutor() { + auto flagValue = enableBackgroundExecutor_.load(); if (!flagValue.has_value()) { // This block is not exclusive but it is not necessary. @@ -110,10 +110,46 @@ bool ReactNativeFeatureFlagsAccessor::batchRenderingUpdatesInEventLoop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(4, "batchRenderingUpdatesInEventLoop"); + markFlagAsAccessed(4, "enableBackgroundExecutor"); - flagValue = currentProvider_->batchRenderingUpdatesInEventLoop(); - batchRenderingUpdatesInEventLoop_ = flagValue; + flagValue = currentProvider_->enableBackgroundExecutor(); + enableBackgroundExecutor_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { + auto flagValue = useModernRuntimeScheduler_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(5, "useModernRuntimeScheduler"); + + flagValue = currentProvider_->useModernRuntimeScheduler(); + useModernRuntimeScheduler_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { + auto flagValue = enableMicrotasks_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(6, "enableMicrotasks"); + + flagValue = currentProvider_->enableMicrotasks(); + enableMicrotasks_ = flagValue; } return flagValue.value(); @@ -128,7 +164,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSpannableBuildingUnification() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(5, "enableSpannableBuildingUnification"); + markFlagAsAccessed(7, "enableSpannableBuildingUnification"); flagValue = currentProvider_->enableSpannableBuildingUnification(); enableSpannableBuildingUnification_ = flagValue; @@ -146,7 +182,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomDrawOrderFabric() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(6, "enableCustomDrawOrderFabric"); + markFlagAsAccessed(8, "enableCustomDrawOrderFabric"); flagValue = currentProvider_->enableCustomDrawOrderFabric(); enableCustomDrawOrderFabric_ = flagValue; @@ -164,7 +200,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFixForClippedSubviewsCrash() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(7, "enableFixForClippedSubviewsCrash"); + markFlagAsAccessed(9, "enableFixForClippedSubviewsCrash"); flagValue = currentProvider_->enableFixForClippedSubviewsCrash(); enableFixForClippedSubviewsCrash_ = flagValue; @@ -182,7 +218,7 @@ bool ReactNativeFeatureFlagsAccessor::inspectorEnableCxxInspectorPackagerConnect // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(8, "inspectorEnableCxxInspectorPackagerConnection"); + markFlagAsAccessed(10, "inspectorEnableCxxInspectorPackagerConnection"); flagValue = currentProvider_->inspectorEnableCxxInspectorPackagerConnection(); inspectorEnableCxxInspectorPackagerConnection_ = flagValue; @@ -200,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::inspectorEnableModernCDPRegistry() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(9, "inspectorEnableModernCDPRegistry"); + markFlagAsAccessed(11, "inspectorEnableModernCDPRegistry"); flagValue = currentProvider_->inspectorEnableModernCDPRegistry(); inspectorEnableModernCDPRegistry_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index b177bfe53b6b33..e6a98054acd0ea 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<95487968b66d40e1ec53936b06084931>> + * @generated SignedSource<> */ /** @@ -32,10 +32,12 @@ class ReactNativeFeatureFlagsAccessor { ReactNativeFeatureFlagsAccessor(); bool commonTestFlag(); + bool androidEnablePendingFabricTransactions(); + bool batchRenderingUpdatesInEventLoop(); + bool destroyFabricSurfacesInReactInstanceManager(); bool enableBackgroundExecutor(); bool useModernRuntimeScheduler(); bool enableMicrotasks(); - bool batchRenderingUpdatesInEventLoop(); bool enableSpannableBuildingUnification(); bool enableCustomDrawOrderFabric(); bool enableFixForClippedSubviewsCrash(); @@ -51,13 +53,15 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 10> accessedFeatureFlags_; + std::array, 12> accessedFeatureFlags_; std::atomic> commonTestFlag_; + std::atomic> androidEnablePendingFabricTransactions_; + std::atomic> batchRenderingUpdatesInEventLoop_; + std::atomic> destroyFabricSurfacesInReactInstanceManager_; std::atomic> enableBackgroundExecutor_; std::atomic> useModernRuntimeScheduler_; std::atomic> enableMicrotasks_; - std::atomic> batchRenderingUpdatesInEventLoop_; std::atomic> enableSpannableBuildingUnification_; std::atomic> enableCustomDrawOrderFabric_; std::atomic> enableFixForClippedSubviewsCrash_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ce2aadae3b63ce..e9d8d4a0f8fd69 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4832483bb3648380f2bb9312311f579c>> + * @generated SignedSource<<8a0f2e61752ae83a7a3032939f4a59fa>> */ /** @@ -31,19 +31,27 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } - bool enableBackgroundExecutor() override { + bool androidEnablePendingFabricTransactions() override { return false; } - bool useModernRuntimeScheduler() override { + bool batchRenderingUpdatesInEventLoop() override { return false; } - bool enableMicrotasks() override { + bool destroyFabricSurfacesInReactInstanceManager() override { return false; } - bool batchRenderingUpdatesInEventLoop() override { + bool enableBackgroundExecutor() override { + return false; + } + + bool useModernRuntimeScheduler() override { + return false; + } + + bool enableMicrotasks() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index f6f55b5fc15e4a..bdf24ccb7736b8 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9194d576893a97a6d9b88f5a32c71197>> */ /** @@ -26,10 +26,12 @@ class ReactNativeFeatureFlagsProvider { virtual ~ReactNativeFeatureFlagsProvider() = default; virtual bool commonTestFlag() = 0; + virtual bool androidEnablePendingFabricTransactions() = 0; + virtual bool batchRenderingUpdatesInEventLoop() = 0; + virtual bool destroyFabricSurfacesInReactInstanceManager() = 0; virtual bool enableBackgroundExecutor() = 0; virtual bool useModernRuntimeScheduler() = 0; virtual bool enableMicrotasks() = 0; - virtual bool batchRenderingUpdatesInEventLoop() = 0; virtual bool enableSpannableBuildingUnification() = 0; virtual bool enableCustomDrawOrderFabric() = 0; virtual bool enableFixForClippedSubviewsCrash() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp index 03a60f9bdac05c..6c35e6b91bb579 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp @@ -86,6 +86,38 @@ struct JNIArgs { std::vector globalRefs_; }; +jsi::Value createJSRuntimeError( + jsi::Runtime& runtime, + const std::string& message) { + return runtime.global() + .getPropertyAsFunction(runtime, "Error") + .call(runtime, message); +} + +jsi::Value createRejectionError(jsi::Runtime& rt, const folly::dynamic& args) { + react_native_assert( + args.size() == 1 && "promise reject should has only one argument"); + + auto value = jsi::valueFromDynamic(rt, args[0]); + react_native_assert(value.isObject() && "promise reject should return a map"); + + const jsi::Object& valueAsObject = value.asObject(rt); + + auto messageProperty = valueAsObject.getProperty(rt, "message"); + auto jsError = + createJSRuntimeError(rt, messageProperty.asString(rt).utf8(rt)); + + auto jsErrorAsObject = jsError.asObject(rt); + auto propertyNames = valueAsObject.getPropertyNames(rt); + for (size_t i = 0; i < propertyNames.size(rt); ++i) { + auto propertyName = jsi::PropNameID::forString( + rt, propertyNames.getValueAtIndex(rt, i).asString(rt)); + jsErrorAsObject.setProperty( + rt, propertyName, valueAsObject.getProperty(rt, propertyName)); + } + return jsError; +} + auto createJavaCallback( jsi::Runtime& rt, jsi::Function&& function, @@ -98,7 +130,6 @@ auto createJavaCallback( LOG(FATAL) << "Callback arg cannot be called more than once"; return; } - callback->call([args = std::move(args)]( jsi::Runtime& rt, jsi::Function& jsFunction) { std::vector jsArgs; @@ -112,6 +143,26 @@ auto createJavaCallback( }); } +auto createJavaRejectCallback( + jsi::Runtime& rt, + jsi::Function&& function, + std::shared_ptr jsInvoker) { + std::optional> callback( + {rt, std::move(function), std::move(jsInvoker)}); + return JCxxCallbackImpl::newObjectCxxArgs( + [callback = std::move(callback)](folly::dynamic args) mutable { + if (!callback) { + LOG(FATAL) << "Callback arg cannot be called more than once"; + return; + } + callback->call([args = std::move(args)]( + jsi::Runtime& rt, jsi::Function& jsFunction) { + jsFunction.call(rt, createRejectionError(rt, args)); + }); + callback = std::nullopt; + }); +} + struct JPromiseImpl : public jni::JavaClass { constexpr static auto kJavaDescriptor = "Lcom/facebook/react/bridge/PromiseImpl;"; @@ -407,14 +458,6 @@ jsi::Value convertFromJMapToValue(JNIEnv* env, jsi::Runtime& rt, jobject arg) { return jsi::valueFromDynamic(rt, result->cthis()->consume()); } -jsi::Value createJSRuntimeError( - jsi::Runtime& runtime, - const std::string& message) { - return runtime.global() - .getPropertyAsFunction(runtime, "Error") - .call(runtime, message); -} - /** * Creates JSError with current JS runtime stack and Throwable stack trace. */ @@ -855,7 +898,7 @@ jsi::Value JavaTurboModule::invokeJavaMethod( runtime, args[0].getObject(runtime).getFunction(runtime), jsInvoker_); - auto reject = createJavaCallback( + auto reject = createJavaRejectCallback( runtime, args[1].getObject(runtime).getFunction(runtime), jsInvoker_); diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h index 66ebd30f92a4f4..cb020a852e6a1a 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h @@ -19,7 +19,7 @@ namespace facebook::react { struct JTurboModule : jni::JavaClass { static auto constexpr kJavaDescriptor = - "Lcom/facebook/react/internal/turbomodule/core/interfaces/TurboModule;"; + "Lcom/facebook/react/turbomodule/core/interfaces/TurboModule;"; }; class JSI_EXPORT JavaTurboModule : public TurboModule { diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm index be0846fff24ad3..6f6a6b835e0301 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm @@ -219,7 +219,10 @@ id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, s NSString *message = jsErrorDetails[@"message"]; auto jsError = createJSRuntimeError(runtime, [message UTF8String]); - jsError.asObject(runtime).setProperty(runtime, "cause", convertObjCObjectToJSIValue(runtime, jsErrorDetails)); + for (NSString *key in jsErrorDetails) { + id value = jsErrorDetails[key]; + jsError.asObject(runtime).setProperty(runtime, [key UTF8String], convertObjCObjectToJSIValue(runtime, value)); + } return jsError; } diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm index d93d606fce4117..9a34ab1ab7d357 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm @@ -674,7 +674,7 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass */ if (_bridge) { [(id)module setValue:_bridge forKey:@"bridge"]; - } else if (_bridgeProxy && [self _isLegacyModuleClass:[module class]]) { + } else if (_bridgeProxy) { [(id)module setValue:_bridgeProxy forKey:@"bridge"]; } } @catch (NSException *exception) { diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 67f799f556c1ee..027970860dad27 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2466af777a1d69da2c3810e5a2d53a70>> + * @generated SignedSource<<6d0bae7c1b0d126e58c8b9bdeaf9247e>> */ /** @@ -40,6 +40,21 @@ bool NativeReactNativeFeatureFlags::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool NativeReactNativeFeatureFlags::androidEnablePendingFabricTransactions( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::androidEnablePendingFabricTransactions(); +} + +bool NativeReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + +bool NativeReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager(); +} + bool NativeReactNativeFeatureFlags::enableBackgroundExecutor( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableBackgroundExecutor(); @@ -55,11 +70,6 @@ bool NativeReactNativeFeatureFlags::enableMicrotasks( return ReactNativeFeatureFlags::enableMicrotasks(); } -bool NativeReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop( - jsi::Runtime& /*runtime*/) { - return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); -} - bool NativeReactNativeFeatureFlags::enableSpannableBuildingUnification( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableSpannableBuildingUnification(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 203dfce52c07e1..0b63229c4fde4f 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<30481dc57cc7f389d1ed87d097caedda>> + * @generated SignedSource<> */ /** @@ -32,14 +32,18 @@ class NativeReactNativeFeatureFlags bool commonTestFlag(jsi::Runtime& runtime); + bool androidEnablePendingFabricTransactions(jsi::Runtime& runtime); + + bool batchRenderingUpdatesInEventLoop(jsi::Runtime& runtime); + + bool destroyFabricSurfacesInReactInstanceManager(jsi::Runtime& runtime); + bool enableBackgroundExecutor(jsi::Runtime& runtime); bool useModernRuntimeScheduler(jsi::Runtime& runtime); bool enableMicrotasks(jsi::Runtime& runtime); - bool batchRenderingUpdatesInEventLoop(jsi::Runtime& runtime); - bool enableSpannableBuildingUnification(jsi::Runtime& runtime); bool enableCustomDrawOrderFabric(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java index 6b05e380a21ee1..92d5687d7e7e5b 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java @@ -19,7 +19,7 @@ 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; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputEventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputEventEmitter.cpp index 88ae3f35a221c6..2180019a175333 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputEventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputEventEmitter.cpp @@ -36,6 +36,56 @@ static jsi::Value textInputMetricsPayload( return payload; }; +static jsi::Value textInputMetricsScrollPayload( + jsi::Runtime& runtime, + const TextInputMetrics& textInputMetrics) { + auto payload = jsi::Object(runtime); + + { + auto contentOffset = jsi::Object(runtime); + contentOffset.setProperty(runtime, "x", textInputMetrics.contentOffset.x); + contentOffset.setProperty(runtime, "y", textInputMetrics.contentOffset.y); + payload.setProperty(runtime, "contentOffset", contentOffset); + } + + { + auto contentInset = jsi::Object(runtime); + contentInset.setProperty(runtime, "top", textInputMetrics.contentInset.top); + contentInset.setProperty( + runtime, "left", textInputMetrics.contentInset.left); + contentInset.setProperty( + runtime, "bottom", textInputMetrics.contentInset.bottom); + contentInset.setProperty( + runtime, "right", textInputMetrics.contentInset.right); + payload.setProperty(runtime, "contentInset", contentInset); + } + + { + auto contentSize = jsi::Object(runtime); + contentSize.setProperty( + runtime, "width", textInputMetrics.contentSize.width); + contentSize.setProperty( + runtime, "height", textInputMetrics.contentSize.height); + payload.setProperty(runtime, "contentSize", contentSize); + } + + { + auto layoutMeasurement = jsi::Object(runtime); + layoutMeasurement.setProperty( + runtime, "width", textInputMetrics.layoutMeasurement.width); + layoutMeasurement.setProperty( + runtime, "height", textInputMetrics.layoutMeasurement.height); + payload.setProperty(runtime, "layoutMeasurement", layoutMeasurement); + } + + payload.setProperty( + runtime, + "zoomScale", + textInputMetrics.zoomScale ? textInputMetrics.zoomScale : 1); + + return payload; +}; + static jsi::Value textInputMetricsContentSizePayload( jsi::Runtime& runtime, const TextInputMetrics& textInputMetrics) { @@ -140,7 +190,9 @@ void TextInputEventEmitter::onKeyPressSync( void TextInputEventEmitter::onScroll( const TextInputMetrics& textInputMetrics) const { - dispatchTextInputEvent("scroll", textInputMetrics); + dispatchEvent("scroll", [textInputMetrics](jsi::Runtime& runtime) { + return textInputMetricsScrollPayload(runtime, textInputMetrics); + }); } void TextInputEventEmitter::dispatchTextInputEvent( diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp index 3d55f25c38cb9c..358fb3773ebf99 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp @@ -140,6 +140,14 @@ BaseViewProps::BaseViewProps( "shadowRadius", sourceProps.shadowRadius, {})), + cursor( + CoreFeatures::enablePropIteratorSetter ? sourceProps.cursor + : convertRawProp( + context, + rawProps, + "cursor", + sourceProps.cursor, + {})), transform( CoreFeatures::enablePropIteratorSetter ? sourceProps.transform : convertRawProp( @@ -281,6 +289,7 @@ void BaseViewProps::setProp( RAW_SET_PROP_SWITCH_CASE_BASIC(collapsable); RAW_SET_PROP_SWITCH_CASE_BASIC(removeClippedSubviews); RAW_SET_PROP_SWITCH_CASE_BASIC(experimental_layoutConformance); + RAW_SET_PROP_SWITCH_CASE_BASIC(cursor); // events field VIEW_EVENT_CASE(PointerEnter); VIEW_EVENT_CASE(PointerEnterCapture); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h index b7bdd9afc7eb55..b386b988390c58 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h @@ -52,6 +52,8 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps { Float shadowOpacity{}; Float shadowRadius{3}; + Cursor cursor{}; + // Transform Transform transform{}; TransformOrigin transformOrigin{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp index fe11e92e1a5771..9c92eac7ccc9e9 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp @@ -60,7 +60,7 @@ void ViewShadowNode::initialize() noexcept { viewProps.accessibilityElementsHidden || viewProps.accessibilityViewIsModal || viewProps.importantForAccessibility != ImportantForAccessibility::Auto || - viewProps.removeClippedSubviews || + viewProps.removeClippedSubviews || viewProps.cursor != Cursor::Auto || HostPlatformViewTraitsInitializer::formsStackingContext(viewProps); bool formsView = formsStackingContext || diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index aa6fb367daab7d..bbd9f46cfafa6a 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -705,6 +705,28 @@ inline void fromRawValue( react_native_expect(false); } +inline void fromRawValue( + const PropsParserContext& context, + const RawValue& value, + Cursor& result) { + result = Cursor::Auto; + react_native_expect(value.hasType()); + if (!value.hasType()) { + return; + } + auto stringValue = (std::string)value; + if (stringValue == "auto") { + result = Cursor::Auto; + return; + } + if (stringValue == "pointer") { + result = Cursor::Pointer; + return; + } + LOG(ERROR) << "Could not parse Cursor:" << stringValue; + react_native_expect(false); +} + inline void fromRawValue( const PropsParserContext& /*context*/, const RawValue& value, diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h index 162f2292cc6a64..09f98979517a13 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h @@ -91,6 +91,8 @@ enum class BorderCurve : uint8_t { Circular, Continuous }; enum class BorderStyle : uint8_t { Solid, Dotted, Dashed }; +enum class Cursor : uint8_t { Auto, Pointer }; + enum class LayoutConformance : uint8_t { Undefined, Classic, Strict }; template diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h index 0aa6a75b92e0ba..6d63cdad1e6da7 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h @@ -48,10 +48,10 @@ inline static NSURL *NSURLFromImageSource(const facebook::react::ImageSource &im { // `NSURL` has a history of crashing with bad input, so let's be safe. @try { - NSString *urlString = [NSString stringWithCString:imageSource.uri.c_str() encoding:NSASCIIStringEncoding]; + NSString *urlString = [NSString stringWithUTF8String:imageSource.uri.c_str()]; if (!imageSource.bundle.empty()) { - NSString *bundle = [NSString stringWithCString:imageSource.bundle.c_str() encoding:NSASCIIStringEncoding]; + NSString *bundle = [NSString stringWithUTF8String:imageSource.bundle.c_str()]; urlString = [NSString stringWithFormat:@"%@.bundle/%@", bundle, urlString]; } diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp index f858acafc6da70..d7dd1bcd916404 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp @@ -41,4 +41,17 @@ Number MountingTransaction::getNumber() const { return number_; } +void MountingTransaction::mergeWith(MountingTransaction&& transaction) { + react_native_assert(transaction.getSurfaceId() == surfaceId_); + number_ = transaction.getNumber(); + mutations_.insert( + mutations_.end(), + std::make_move_iterator(transaction.mutations_.begin()), + std::make_move_iterator(transaction.mutations_.end())); + + // TODO T186641819: Telemetry for merged transactions is not supported, use + // the latest instance + telemetry_ = std::move(transaction.telemetry_); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h index 3d90076ce8b3a6..277e9f4e307c1e 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h @@ -76,6 +76,15 @@ class MountingTransaction final { */ Number getNumber() const; + /* + * Merges the given transaction in the current transaction, so they + * can be executed atomatically as a single transaction. + * + * This is required for Android UI, which needs to separately apply + * each ShadowTree mutation due to differences in props representation. + */ + void mergeWith(MountingTransaction&& transaction); + private: SurfaceId surfaceId_; Number number_; diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h deleted file mode 100644 index a73bb52ce3c347..00000000000000 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h +++ /dev/null @@ -1,34 +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. - */ - -#include - -namespace facebook::react { - -inline static void handleFatalError( - jsi::Runtime& runtime, - const jsi::JSError& error) { - auto reportFatalError = "reportFatalError"; - auto errorUtils = runtime.global().getProperty(runtime, "ErrorUtils"); - if (errorUtils.isUndefined() || !errorUtils.isObject() || - !errorUtils.getObject(runtime).hasProperty(runtime, reportFatalError)) { - // ErrorUtils was not set up. This probably means the bundle didn't - // load properly. - throw jsi::JSError( - runtime, - "ErrorUtils is not set up properly. Something probably went wrong trying to load the JS bundle. Trying to report error " + - error.getMessage(), - error.getStack()); - } - - auto func = errorUtils.asObject(runtime).getPropertyAsFunction( - runtime, reportFatalError); - - func.call(runtime, error.value()); -} - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp index 79be477d2c240a..77842df703441d 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp @@ -13,7 +13,6 @@ #include #include #include -#include "ErrorUtils.h" namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp index 812eac2456d8c0..e27517b1ed261f 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp @@ -8,9 +8,9 @@ #include "RuntimeScheduler_Legacy.h" #include "SchedulerPriorityUtils.h" +#include #include #include -#include "ErrorUtils.h" namespace facebook::react { @@ -136,7 +136,7 @@ void RuntimeScheduler_Legacy::callExpiredTasks(jsi::Runtime& runtime) { executeTask(runtime, topPriorityTask, didUserCallbackTimeout); } } catch (jsi::JSError& error) { - handleFatalError(runtime, error); + handleJSError(runtime, error, true); } currentPriority_ = previousPriority; @@ -182,7 +182,7 @@ void RuntimeScheduler_Legacy::startWorkLoop(jsi::Runtime& runtime) { executeTask(runtime, topPriorityTask, didUserCallbackTimeout); } } catch (jsi::JSError& error) { - handleFatalError(runtime, error); + handleJSError(runtime, error, true); } currentPriority_ = previousPriority; diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp index 4a30a2ff5c4034..5ec7332d2d929c 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp @@ -12,7 +12,6 @@ #include #include #include -#include "ErrorUtils.h" namespace facebook::react { @@ -104,7 +103,7 @@ bool RuntimeScheduler_Modern::getShouldYield() const noexcept { std::shared_lock lock(schedulingMutex_); return syncTaskRequests_ > 0 || - (!taskQueue_.empty() && taskQueue_.top() != currentTask_); + (!taskQueue_.empty() && taskQueue_.top().get() != currentTask_); } bool RuntimeScheduler_Modern::getIsSynchronous() const noexcept { @@ -144,9 +143,8 @@ void RuntimeScheduler_Modern::executeNowOnTheSameThread( auto priority = SchedulerPriority::ImmediatePriority; auto expirationTime = currentTime + timeoutForSchedulerPriority(priority); - auto task = std::make_shared( - priority, std::move(callback), expirationTime); + auto task = Task{priority, std::move(callback), expirationTime}; executeTask(runtime, task, currentTime); isSynchronous_ = false; @@ -231,21 +229,17 @@ void RuntimeScheduler_Modern::startWorkLoop( auto previousPriority = currentPriority_; - try { - while (syncTaskRequests_ == 0) { - auto currentTime = now_(); - auto topPriorityTask = selectTask(currentTime, onlyExpired); + while (syncTaskRequests_ == 0) { + auto currentTime = now_(); + auto topPriorityTask = selectTask(currentTime, onlyExpired); - if (!topPriorityTask) { - // No pending work to do. - // Events will restart the loop when necessary. - break; - } - - executeTask(runtime, topPriorityTask, currentTime); + if (!topPriorityTask) { + // No pending work to do. + // Events will restart the loop when necessary. + break; } - } catch (jsi::JSError& error) { - handleFatalError(runtime, error); + + executeTask(runtime, *topPriorityTask, currentTime); } currentPriority_ = previousPriority; @@ -280,19 +274,19 @@ std::shared_ptr RuntimeScheduler_Modern::selectTask( void RuntimeScheduler_Modern::executeTask( jsi::Runtime& runtime, - const std::shared_ptr& task, + Task& task, RuntimeSchedulerTimePoint currentTime) { - auto didUserCallbackTimeout = task->expirationTime <= currentTime; + auto didUserCallbackTimeout = task.expirationTime <= currentTime; SystraceSection s( "RuntimeScheduler::executeTask", "priority", - serialize(task->priority), + serialize(task.priority), "didUserCallbackTimeout", didUserCallbackTimeout); - currentTask_ = task; - currentPriority_ = task->priority; + currentTask_ = &task; + currentPriority_ = task.priority; executeMacrotask(runtime, task, didUserCallbackTimeout); @@ -305,6 +299,8 @@ void RuntimeScheduler_Modern::executeTask( // "Update the rendering" step. updateRendering(); } + + currentTask_ = nullptr; } /** @@ -326,16 +322,20 @@ void RuntimeScheduler_Modern::updateRendering() { void RuntimeScheduler_Modern::executeMacrotask( jsi::Runtime& runtime, - std::shared_ptr task, + Task& task, bool didUserCallbackTimeout) const { SystraceSection s("RuntimeScheduler::executeMacrotask"); - auto result = task->execute(runtime, didUserCallbackTimeout); + try { + auto result = task.execute(runtime, didUserCallbackTimeout); - if (result.isObject() && result.getObject(runtime).isFunction(runtime)) { - // If the task returned a continuation callback, we re-assign it to the task - // and keep the task in the queue. - task->callback = result.getObject(runtime).getFunction(runtime); + if (result.isObject() && result.getObject(runtime).isFunction(runtime)) { + // If the task returned a continuation callback, we re-assign it to the + // task and keep the task in the queue. + task.callback = result.getObject(runtime).getFunction(runtime); + } + } catch (jsi::JSError& error) { + handleJSError(runtime, error, true); } } diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h index 34c8eb4cf16cbf..57a2449df6a951 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h @@ -139,7 +139,7 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase { TaskPriorityComparer> taskQueue_; - std::shared_ptr currentTask_; + Task* currentTask_{}; /** * This protects the access to `taskQueue_` and `isWorkLoopScheduled_`. @@ -168,12 +168,12 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase { */ void executeTask( jsi::Runtime& runtime, - const std::shared_ptr& task, + Task& task, RuntimeSchedulerTimePoint currentTime); void executeMacrotask( jsi::Runtime& runtime, - std::shared_ptr task, + Task& task, bool didUserCallbackTimeout) const; void updateRendering(); diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/Task.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/Task.cpp index 24a0357ac83f43..6816e38fa555f0 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/Task.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/Task.cpp @@ -32,20 +32,22 @@ jsi::Value Task::execute(jsi::Runtime& runtime, bool didUserCallbackTimeout) { return result; } - auto& cbVal = callback.value(); + // We get the value of the callback and reset it immediately to avoid it being + // called more than once (including when the callback throws). + auto originalCallback = std::move(*callback); + callback.reset(); - if (cbVal.index() == 0) { + if (originalCallback.index() == 0) { // Callback in JavaScript is expecting a single bool parameter. // React team plans to remove it in the future when a scheduler bug on web // is resolved. - result = - std::get(cbVal).call(runtime, {didUserCallbackTimeout}); + result = std::get(originalCallback) + .call(runtime, {didUserCallbackTimeout}); } else { // Calling a raw callback - std::get(cbVal)(runtime); + std::get(originalCallback)(runtime); } - // Destroying callback to prevent calling it twice. - callback.reset(); + return result; } diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp index 292b62c8916990..ddaf76c8f7425f 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp @@ -1021,6 +1021,52 @@ TEST_P(RuntimeSchedulerTest, modernTwoThreadsRequestAccessToTheRuntime) { EXPECT_EQ(stubQueue_->size(), 0); } +TEST_P(RuntimeSchedulerTest, errorInTaskShouldNotStopMicrotasks) { + // Only for modern runtime scheduler + if (!GetParam()) { + return; + } + + auto microtaskRan = false; + auto taskRan = false; + + auto callback = createHostFunctionFromLambda([&](bool /* unused */) { + taskRan = true; + + auto microtaskCallback = jsi::Function::createFromHostFunction( + *runtime_, + jsi::PropNameID::forUtf8(*runtime_, "microtask1"), + 3, + [&](jsi::Runtime& /*unused*/, + const jsi::Value& /*unused*/, + const jsi::Value* /*arguments*/, + size_t /*unused*/) -> jsi::Value { + microtaskRan = true; + return jsi::Value::undefined(); + }); + + runtime_->queueMicrotask(microtaskCallback); + + throw jsi::JSError(*runtime_, "Test error"); + + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + EXPECT_EQ(taskRan, false); + EXPECT_EQ(microtaskRan, false); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_EQ(taskRan, 1); + EXPECT_EQ(microtaskRan, 1); + EXPECT_EQ(stubQueue_->size(), 0); + EXPECT_EQ(stubErrorUtils_->getReportFatalCallCount(), 1); +} + INSTANTIATE_TEST_SUITE_P( UseModernRuntimeScheduler, RuntimeSchedulerTest, diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index ee4ed0a1f40e01..a70abde5cf72aa 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -273,6 +273,10 @@ void Scheduler::uiManagerDidFinishTransaction( SystraceSection s("Scheduler::uiManagerDidFinishTransaction"); if (delegate_ != nullptr) { + // This is no-op on all platforms except for Android where we need to + // observe each transaction to be able to mount correctly. + delegate_->schedulerDidFinishTransaction(mountingCoordinator); + auto weakRuntimeScheduler = contextContainer_->find>( "RuntimeScheduler"); @@ -283,13 +287,14 @@ void Scheduler::uiManagerDidFinishTransaction( runtimeScheduler->scheduleRenderingUpdate( [delegate = delegate_, mountingCoordinator = std::move(mountingCoordinator)]() { - delegate->schedulerDidFinishTransaction(mountingCoordinator); + delegate->schedulerShouldRenderTransactions(mountingCoordinator); }); } else { - delegate_->schedulerDidFinishTransaction(mountingCoordinator); + delegate_->schedulerShouldRenderTransactions(mountingCoordinator); } } } + void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode& shadowNode) { SystraceSection s("Scheduler::uiManagerDidCreateShadowNode"); diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h index d103bf850ec12f..338d637da94488 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h @@ -28,6 +28,17 @@ class SchedulerDelegate { virtual void schedulerDidFinishTransaction( const MountingCoordinator::Shared& mountingCoordinator) = 0; + /* + * Called when the runtime scheduler decides that one-or-more previously + * finished transactions should now be flushed to the screen (atomically). + * + * This is a separate callback from didFinishTransaction as the Android UI + * mounting layer needs to be able toobserve each created ShadowTree to + * correctly apply changes, due to changes in Props representation. + */ + virtual void schedulerShouldRenderTransactions( + const MountingCoordinator::Shared& mountingCoordinator) = 0; + /* * Called right after a new ShadowNode was created. */ diff --git a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec index 93f5f2f120eb1e..141ad6a1eaf9c4 100644 --- a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec +++ b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec @@ -42,7 +42,7 @@ Pod::Spec.new do |s| s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags if ENV['USE_FRAMEWORKS'] - s.header_mappings_dir = './' + s.header_mappings_dir = '../../' s.module_name = 'React_RuntimeCore' end diff --git a/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp b/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp index 13308c81c26704..101f226f92c15b 100644 --- a/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp @@ -168,7 +168,6 @@ std::unique_ptr HermesInstance::createJSRuntime( .withAllocInYoung(false) .withRevertToYGAtTTI(true) .build()) - .withES6Proxy(false) .withEnableSampleProfiling(true) .withMicrotaskQueue(ReactNativeFeatureFlags::enableMicrotasks()) .withVMExperimentFlags(vmExperimentFlags); diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm index de2e016b95afc4..cadf6d61cdca8e 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm @@ -58,7 +58,8 @@ - (void)setUp turboModuleManagerDelegate:OCMProtocolMock(@protocol(RCTTurboModuleManagerDelegate)) jsEngineProvider:^std::shared_ptr() { return std::make_shared(); - }]; + } + launchOptions:nil]; } - (void)tearDown diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost+Internal.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost+Internal.h index 4432aa0cce498d..e1708784a45340 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost+Internal.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost+Internal.h @@ -9,8 +9,6 @@ #import "RCTContextContainerHandling.h" -typedef NSURL * (^RCTHostBundleURLProvider)(void); - @interface RCTHost (Internal) - (void)registerSegmentWithId:(NSNumber *)segmentId path:(NSString *)path; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h index 6b869377a836a5..87f6de0b7fa045 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h @@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN @protocol RCTTurboModuleManagerDelegate; +typedef NSURL *_Nullable (^RCTHostBundleURLProvider)(void); + // Runtime API @protocol RCTHostDelegate @@ -45,13 +47,24 @@ typedef std::shared_ptr (^RCTHostJSEngineProv @interface RCTHost : NSObject +- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider + hostDelegate:(id)hostDelegate + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider + launchOptions:(nullable NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; + - (instancetype)initWithBundleURL:(NSURL *)bundleURL hostDelegate:(id)hostDelegate turboModuleManagerDelegate:(id)turboModuleManagerDelegate - jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider NS_DESIGNATED_INITIALIZER; + jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider + launchOptions:(nullable NSDictionary *)launchOptions __deprecated; @property (nonatomic, weak, nullable) id runtimeDelegate; +@property (nonatomic, readonly) RCTSurfacePresenter *surfacePresenter; + +@property (nonatomic, readonly) RCTModuleRegistry *moduleRegistry; + - (void)start; - (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args; @@ -64,11 +77,11 @@ typedef std::shared_ptr (^RCTHostJSEngineProv - (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)properties; -- (RCTSurfacePresenter *)getSurfacePresenter; +- (RCTSurfacePresenter *)getSurfacePresenter __attribute__((deprecated("Use `surfacePresenter` property instead."))); // Native module API -- (RCTModuleRegistry *)getModuleRegistry; +- (RCTModuleRegistry *)getModuleRegistry __attribute__((deprecated("Use `moduleRegistry` property instead."))); @end diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index 85602c4c8a6f8a..cddebbe109fba7 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -58,6 +58,8 @@ @implementation RCTHost { RCTHostBundleURLProvider _bundleURLProvider; RCTHostJSEngineProvider _jsEngineProvider; + NSDictionary *_launchOptions; + // All the surfaces that need to be started after main bundle execution NSMutableArray *_surfaceStartBuffer; std::mutex _surfaceStartBufferMutex; @@ -77,14 +79,31 @@ + (void)initialize _RCTInitializeJSThreadConstantInternal(); } -/** - Host initialization should not be resource intensive. A host may be created before any intention of using React Native - has been expressed. - */ - (instancetype)initWithBundleURL:(NSURL *)bundleURL hostDelegate:(id)hostDelegate turboModuleManagerDelegate:(id)turboModuleManagerDelegate jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider + launchOptions:(nullable NSDictionary *)launchOptions +{ + return [self + initWithBundleURLProvider:^{ + return bundleURL; + } + hostDelegate:hostDelegate + turboModuleManagerDelegate:turboModuleManagerDelegate + jsEngineProvider:jsEngineProvider + launchOptions:launchOptions]; +} + +/** + Host initialization should not be resource intensive. A host may be created before any intention of using React Native + has been expressed. + */ +- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider + hostDelegate:(id)hostDelegate + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider + launchOptions:(nullable NSDictionary *)launchOptions { if (self = [super init]) { _hostDelegate = hostDelegate; @@ -93,9 +112,9 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL _bundleManager = [RCTBundleManager new]; _moduleRegistry = [RCTModuleRegistry new]; _jsEngineProvider = [jsEngineProvider copy]; + _launchOptions = [launchOptions copy]; __weak RCTHost *weakSelf = self; - auto bundleURLGetter = ^NSURL *() { RCTHost *strongSelf = weakSelf; @@ -107,7 +126,7 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL }; auto bundleURLSetter = ^(NSURL *bundleURL_) { - [weakSelf _setBundleURL:bundleURL]; + [weakSelf _setBundleURL:bundleURL_]; }; auto defaultBundleURLGetter = ^NSURL *() @@ -120,7 +139,6 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL return strongSelf->_bundleURLProvider(); }; - [self _setBundleURL:bundleURL]; [_bundleManager setBridgelessBundleURLGetter:bundleURLGetter andSetter:bundleURLSetter andDefaultGetter:defaultBundleURLGetter]; @@ -166,6 +184,9 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL - (void)start { + if (_bundleURLProvider) { + [self _setBundleURL:_bundleURLProvider()]; + } auto &inspectorFlags = jsinspector_modern::InspectorFlags::getInstance(); if (inspectorFlags.getEnableModernCDPRegistry() && !_inspectorPageId.has_value()) { _inspectorTarget = @@ -204,7 +225,8 @@ - (void)start turboModuleManagerDelegate:_turboModuleManagerDelegate onInitialBundleLoad:_onInitialBundleLoad moduleRegistry:_moduleRegistry - parentInspectorTarget:_inspectorTarget.get()]; + parentInspectorTarget:_inspectorTarget.get() + launchOptions:_launchOptions]; [_hostDelegate hostDidStart:self]; } @@ -212,7 +234,7 @@ - (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName mode:(DisplayMode)displayMode initialProperties:(NSDictionary *)properties { - RCTFabricSurface *surface = [[RCTFabricSurface alloc] initWithSurfacePresenter:[self getSurfacePresenter] + RCTFabricSurface *surface = [[RCTFabricSurface alloc] initWithSurfacePresenter:self.surfacePresenter moduleName:moduleName initialProperties:properties]; surface.surfaceHandler.setDisplayMode(displayMode); @@ -235,16 +257,28 @@ - (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName initial return [self createSurfaceWithModuleName:moduleName mode:DisplayMode::Visible initialProperties:properties]; } -- (RCTModuleRegistry *)getModuleRegistry +- (RCTModuleRegistry *)moduleRegistry { return _moduleRegistry; } -- (RCTSurfacePresenter *)getSurfacePresenter +// Deprecated +- (RCTModuleRegistry *)getModuleRegistry +{ + return self.moduleRegistry; +} + +- (RCTSurfacePresenter *)surfacePresenter { return [_instance surfacePresenter]; } +// Deprecated +- (RCTSurfacePresenter *)getSurfacePresenter +{ + return self.surfacePresenter; +} + - (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args { [_instance callFunctionOnJSModule:moduleName method:method args:args]; @@ -272,11 +306,12 @@ - (void)didReceiveReloadCommand turboModuleManagerDelegate:_turboModuleManagerDelegate onInitialBundleLoad:_onInitialBundleLoad moduleRegistry:_moduleRegistry - parentInspectorTarget:_inspectorTarget.get()]; + parentInspectorTarget:_inspectorTarget.get() + launchOptions:_launchOptions]; [_hostDelegate hostDidStart:self]; for (RCTFabricSurface *surface in [self _getAttachedSurfaces]) { - [surface resetWithSurfacePresenter:[self getSurfacePresenter]]; + [surface resetWithSurfacePresenter:self.surfacePresenter]; } } diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h index a24cc08a496e0e..6eaba0a1a8b782 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h @@ -62,7 +62,8 @@ typedef void (^_Null_unspecified RCTInstanceInitialBundleLoadCompletionBlock)(); turboModuleManagerDelegate:(id)turboModuleManagerDelegate onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad moduleRegistry:(RCTModuleRegistry *)moduleRegistry - parentInspectorTarget:(facebook::react::jsinspector_modern::PageTarget *)parentInspectorTarget; + parentInspectorTarget:(facebook::react::jsinspector_modern::PageTarget *)parentInspectorTarget + launchOptions:(nullable NSDictionary *)launchOptions; - (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index a123105a672ce1..029525a10c033a 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -6,16 +6,18 @@ */ #import "RCTInstance.h" -#import #import #import #import +#import #import #import #import #import +#import +#import #import #import #import @@ -28,6 +30,7 @@ #import #import #import +#import #import #import #import @@ -80,6 +83,7 @@ @implementation RCTInstance { std::mutex _invalidationMutex; std::atomic _valid; RCTJSThreadManager *_jsThreadManager; + NSDictionary *_launchOptions; // APIs supporting interop with native modules and view managers RCTBridgeModuleDecorator *_bridgeModuleDecorator; @@ -96,6 +100,7 @@ - (instancetype)initWithDelegate:(id)delegate onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad moduleRegistry:(RCTModuleRegistry *)moduleRegistry parentInspectorTarget:(jsinspector_modern::PageTarget *)parentInspectorTarget + launchOptions:(nullable NSDictionary *)launchOptions { if (self = [super init]) { _performanceLogger = [RCTPerformanceLogger new]; @@ -121,11 +126,14 @@ - (instancetype)initWithDelegate:(id)delegate [weakSelf callFunctionOnJSModule:moduleName method:methodName args:args]; }]; } + _launchOptions = launchOptions; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_notifyEventDispatcherObserversOfEvent_DEPRECATED:) - name:@"RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED" - object:nil]; + NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; + + [defaultCenter addObserver:self + selector:@selector(_notifyEventDispatcherObserversOfEvent_DEPRECATED:) + name:@"RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED" + object:nil]; [self _start]; } @@ -247,6 +255,7 @@ - (void)_start RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor(); timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); + auto jsCallInvoker = make_shared(bufferedRuntimeExecutor); RCTBridgeProxy *bridgeProxy = [[RCTBridgeProxy alloc] initWithViewRegistry:_bridgeModuleDecorator.viewRegistry_DEPRECATED moduleRegistry:_bridgeModuleDecorator.moduleRegistry @@ -264,15 +273,16 @@ - (void)_start [strongSelf registerSegmentWithId:segmentId path:path]; } } - runtime:_reactInstance->getJavaScriptContext()]; + runtime:_reactInstance->getJavaScriptContext() + launchOptions:_launchOptions]; + bridgeProxy.jsCallInvoker = jsCallInvoker; [RCTBridge setCurrentBridge:(RCTBridge *)bridgeProxy]; // Set up TurboModules - _turboModuleManager = [[RCTTurboModuleManager alloc] - initWithBridgeProxy:bridgeProxy - bridgeModuleDecorator:_bridgeModuleDecorator - delegate:self - jsInvoker:std::make_shared(bufferedRuntimeExecutor)]; + _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridgeProxy:bridgeProxy + bridgeModuleDecorator:_bridgeModuleDecorator + delegate:self + jsInvoker:jsCallInvoker]; _turboModuleManager.runtimeHandler = self; #if RCT_DEV @@ -389,6 +399,24 @@ - (void)_attachBridgelessAPIsToModule:(id)module } } +- (void)handleBundleLoadingError:(NSError *)error +{ + if (!_valid) { + return; + } + + RCTRedBox *redBox = [_turboModuleManager moduleForName:"RedBox"]; + + RCTExecuteOnMainQueue(^{ + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification + object:self + userInfo:@{@"error" : error}]; + [redBox showErrorMessage:[error localizedDescription]]; + + RCTFatal(error); + }); +} + - (void)_loadJSBundle:(NSURL *)sourceURL { #if RCT_DEV_MENU && __has_include() @@ -420,8 +448,7 @@ - (void)_loadJSBundle:(NSURL *)sourceURL } if (error) { - // TODO(T91461138): Properly address bundle loading errors. - RCTLogError(@"RCTInstance: Error while loading bundle: %@", error); + [strongSelf handleBundleLoadingError:error]; [strongSelf invalidate]; return; } diff --git a/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.h b/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.h index e11e88c560a72a..654cf17657c9be 100644 --- a/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.h +++ b/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.h @@ -11,7 +11,7 @@ @property int initCount; @property int invalidateCount; - +@property NSDictionary *launchOptions; @property NSString *jsModuleName; @property NSString *method; @property NSArray *args; diff --git a/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.mm b/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.mm index a6be15a7f03011..cfe0aac191ce8f 100644 --- a/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.mm +++ b/packages/react-native/ReactCommon/react/test_utils/ios/Shims/ShimRCTInstance.mm @@ -24,7 +24,7 @@ - (instancetype)init [ShimRCTInstance class], @selector(initWithDelegate: jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:onInitialBundleLoad:moduleRegistry - :parentInspectorTarget:)); + :parentInspectorTarget:launchOptions:)); RCTSwizzleInstanceSelector([RCTInstance class], [ShimRCTInstance class], @selector(invalidate)); RCTSwizzleInstanceSelector( [RCTInstance class], [ShimRCTInstance class], @selector(callFunctionOnJSModule:method:args:)); @@ -40,7 +40,7 @@ - (void)reset [ShimRCTInstance class], @selector(initWithDelegate: jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:onInitialBundleLoad:moduleRegistry - :parentInspectorTarget:)); + :parentInspectorTarget:launchOptions:)); RCTSwizzleInstanceSelector([RCTInstance class], [ShimRCTInstance class], @selector(invalidate)); RCTSwizzleInstanceSelector( [RCTInstance class], [ShimRCTInstance class], @selector(callFunctionOnJSModule:method:args:)); @@ -55,6 +55,7 @@ - (instancetype)initWithDelegate:(id)delegate onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad moduleRegistry:(RCTModuleRegistry *)moduleRegistry parentInspectorTarget:(facebook::react::jsinspector_modern::PageTarget *)parentInspectorTarget + launchOptions:(NSDictionary *)launchOptions { weakShim.initCount++; return self; diff --git a/packages/react-native/ReactCommon/yoga/Yoga.podspec b/packages/react-native/ReactCommon/yoga/Yoga.podspec index a84876951aa67c..b323b05ed739f7 100644 --- a/packages/react-native/ReactCommon/yoga/Yoga.podspec +++ b/packages/react-native/ReactCommon/yoga/Yoga.podspec @@ -32,7 +32,10 @@ Pod::Spec.new do |spec| spec.requires_arc = false spec.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' - } + }.merge!(ENV['USE_FRAMEWORKS'] != nil ? { + 'HEADER_SEARCH_PATHS' => '"$(PODS_TARGET_SRCROOT)"' +} : {}) + spec.compiler_flags = [ '-fno-omit-frame-pointer', '-fexceptions', diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp index ccec8f187623ea..32a13c9d430664 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp @@ -480,14 +480,10 @@ void layoutAbsoluteDescendants( LayoutData& layoutMarkerData, uint32_t currentDepth, uint32_t generationCount, - float currentNodeMainOffsetFromContainingBlock, - float currentNodeCrossOffsetFromContainingBlock, + float currentNodeLeftOffsetFromContainingBlock, + float currentNodeTopOffsetFromContainingBlock, float containingNodeAvailableInnerWidth, float containingNodeAvailableInnerHeight) { - const FlexDirection mainAxis = resolveDirection( - currentNode->style().flexDirection(), currentNodeDirection); - const FlexDirection crossAxis = - resolveCrossDirection(mainAxis, currentNodeDirection); for (auto child : currentNode->getChildren()) { if (child->style().display() == Display::None) { continue; @@ -516,45 +512,73 @@ void layoutAbsoluteDescendants( currentDepth, generationCount); - const bool isMainAxisRow = isRow(mainAxis); - const bool mainInsetsDefined = isMainAxisRow - ? child->style().horizontalInsetsDefined() - : child->style().verticalInsetsDefined(); - const bool crossInsetsDefined = isMainAxisRow - ? child->style().verticalInsetsDefined() - : child->style().horizontalInsetsDefined(); - - const float childMainOffsetFromParent = mainInsetsDefined - ? (child->getLayout().position(flexStartEdge(mainAxis)) - - currentNodeMainOffsetFromContainingBlock) - : child->getLayout().position(flexStartEdge(mainAxis)); - const float childCrossOffsetFromParent = crossInsetsDefined - ? (child->getLayout().position(flexStartEdge(crossAxis)) - - currentNodeCrossOffsetFromContainingBlock) - : child->getLayout().position(flexStartEdge(crossAxis)); - - child->setLayoutPosition( - childMainOffsetFromParent, flexStartEdge(mainAxis)); - child->setLayoutPosition( - childCrossOffsetFromParent, flexStartEdge(crossAxis)); - - if (needsTrailingPosition(mainAxis)) { - setChildTrailingPosition(currentNode, child, mainAxis); + /* + * At this point the child has its position set but only on its the + * parent's flexStart edge. Additionally, this position should be + * interpreted relative to the containing block of the child if it had + * insets defined. So we need to adjust the position by subtracting the + * the parents offset from the containing block. However, getting that + * offset is complicated since the two nodes can have different main/cross + * axes. + */ + const FlexDirection parentMainAxis = resolveDirection( + currentNode->style().flexDirection(), currentNodeDirection); + const FlexDirection parentCrossAxis = + resolveCrossDirection(parentMainAxis, currentNodeDirection); + + if (needsTrailingPosition(parentMainAxis)) { + const bool mainInsetsDefined = isRow(parentMainAxis) + ? child->style().horizontalInsetsDefined() + : child->style().verticalInsetsDefined(); + setChildTrailingPosition( + mainInsetsDefined ? containingNode : currentNode, + child, + parentMainAxis); } - if (needsTrailingPosition(crossAxis)) { - setChildTrailingPosition(currentNode, child, crossAxis); + if (needsTrailingPosition(parentCrossAxis)) { + const bool crossInsetsDefined = isRow(parentCrossAxis) + ? child->style().horizontalInsetsDefined() + : child->style().verticalInsetsDefined(); + setChildTrailingPosition( + crossInsetsDefined ? containingNode : currentNode, + child, + parentCrossAxis); } + + /* + * At this point we know the left and top physical edges of the child are + * set with positions that are relative to the containing block if insets + * are defined + */ + const float childLeftPosition = + child->getLayout().position(PhysicalEdge::Left); + const float childTopPosition = + child->getLayout().position(PhysicalEdge::Top); + + const float childLeftOffsetFromParent = + child->style().horizontalInsetsDefined() + ? (childLeftPosition - currentNodeLeftOffsetFromContainingBlock) + : childLeftPosition; + const float childTopOffsetFromParent = + child->style().verticalInsetsDefined() + ? (childTopPosition - currentNodeTopOffsetFromContainingBlock) + : childTopPosition; + + child->setLayoutPosition(childLeftOffsetFromParent, PhysicalEdge::Left); + child->setLayoutPosition(childTopOffsetFromParent, PhysicalEdge::Top); } else if ( child->style().positionType() == PositionType::Static && !child->alwaysFormsContainingBlock()) { const Direction childDirection = child->resolveDirection(currentNodeDirection); - const float childMainOffsetFromContainingBlock = - currentNodeMainOffsetFromContainingBlock + - child->getLayout().position(flexStartEdge(mainAxis)); - const float childCrossOffsetFromContainingBlock = - currentNodeCrossOffsetFromContainingBlock + - child->getLayout().position(flexStartEdge(crossAxis)); + // By now all descendants of the containing block that are not absolute + // will have their positions set for left and top. + const float childLeftOffsetFromContainingBlock = + currentNodeLeftOffsetFromContainingBlock + + child->getLayout().position(PhysicalEdge::Left); + const float childTopOffsetFromContainingBlock = + currentNodeTopOffsetFromContainingBlock + + child->getLayout().position(PhysicalEdge::Top); layoutAbsoluteDescendants( containingNode, @@ -564,8 +588,8 @@ void layoutAbsoluteDescendants( layoutMarkerData, currentDepth + 1, generationCount, - childMainOffsetFromContainingBlock, - childCrossOffsetFromContainingBlock, + childLeftOffsetFromContainingBlock, + childTopOffsetFromContainingBlock, containingNodeAvailableInnerWidth, containingNodeAvailableInnerHeight); } diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp index 9b214cf587ff6d..e04111f110c1af 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp @@ -2045,26 +2045,7 @@ static void calculateLayoutImpl( } if (performLayout) { - // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN - // Let the containing block layout its absolute descendants. By definition - // the containing block will not be static unless we are at the root. - if (node->style().positionType() != PositionType::Static || - node->alwaysFormsContainingBlock() || depth == 1) { - layoutAbsoluteDescendants( - node, - node, - isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, - direction, - layoutMarkerData, - depth, - generationCount, - 0.0f, - 0.0f, - availableInnerWidth, - availableInnerHeight); - } - - // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN const bool needsMainTrailingPos = needsTrailingPosition(mainAxis); const bool needsCrossTrailingPos = needsTrailingPosition(crossAxis); @@ -2087,6 +2068,24 @@ static void calculateLayoutImpl( } } } + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN + // Let the containing block layout its absolute descendants. + if (node->style().positionType() != PositionType::Static || + node->alwaysFormsContainingBlock() || depth == 1) { + layoutAbsoluteDescendants( + node, + node, + isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, + direction, + layoutMarkerData, + depth, + generationCount, + 0.0f, + 0.0f, + availableInnerWidth, + availableInnerHeight); + } } } diff --git a/packages/react-native/cli.js b/packages/react-native/cli.js index 7dd4bfb76de8d5..6171a3ab30d880 100755 --- a/packages/react-native/cli.js +++ b/packages/react-native/cli.js @@ -17,6 +17,7 @@ const {get} = require('https'); const {URL} = require('url'); const isNpxRuntime = process.env.npm_lifecycle_event === 'npx'; +const isInitCommand = process.argv[2] === 'init'; const DEFAULT_REGISTRY_HOST = process.env.npm_config_registry ?? 'https://registry.npmjs.org/'; const HEAD = '1000.0.0'; @@ -39,6 +40,18 @@ async function getLatestVersion(registryHost = DEFAULT_REGISTRY_HOST) { }); } +/** + * Warn when users are using `npx react-native init`, to raise awareness of the changes from RFC 0759. + * @see https://github.com/react-native-community/discussions-and-proposals/tree/main/proposals/0759-react-native-frameworks.md + */ +function warnWhenRunningInit() { + if (isInitCommand) { + console.warn( + `\nRunning: ${chalk.grey.bold('npx @react-native-community/cli init')}\n`, + ); + } +} + /** * npx react-native -> @react-native-community/cli * @@ -49,7 +62,12 @@ async function getLatestVersion(registryHost = DEFAULT_REGISTRY_HOST) { * */ async function main() { - if (isNpxRuntime && !process.env.SKIP && currentVersion !== HEAD) { + if ( + isNpxRuntime && + !process.env.SKIP && + currentVersion !== HEAD && + isInitCommand + ) { try { const latest = await getLatestVersion(); if (latest !== currentVersion) { @@ -66,6 +84,9 @@ async function main() { // Ignore errors, since it's a nice to have warning } } + + warnWhenRunningInit(); + return cli.run(name); } diff --git a/packages/react-native/index.js b/packages/react-native/index.js index 2dfeafbb64efd7..f087b702545fcd 100644 --- a/packages/react-native/index.js +++ b/packages/react-native/index.js @@ -27,7 +27,6 @@ import typeof Clipboard from './Libraries/Components/Clipboard/Clipboard'; import typeof DrawerLayoutAndroid from './Libraries/Components/DrawerAndroid/DrawerLayoutAndroid'; import typeof Keyboard from './Libraries/Components/Keyboard/Keyboard'; import typeof KeyboardAvoidingView from './Libraries/Components/Keyboard/KeyboardAvoidingView'; -import typeof PopupMenuAndroid from './Libraries/Components/PopupMenuAndroid/PopupMenuAndroid'; import typeof Pressable from './Libraries/Components/Pressable/Pressable'; import typeof ProgressBarAndroid from './Libraries/Components/ProgressBarAndroid/ProgressBarAndroid'; import typeof RefreshControl from './Libraries/Components/RefreshControl/RefreshControl'; @@ -135,10 +134,6 @@ module.exports = { return require('./Libraries/Components/Keyboard/KeyboardAvoidingView') .default; }, - get PopupMenuAndroid(): PopupMenuAndroid { - return require('./Libraries/Components/PopupMenuAndroid/PopupMenuAndroid') - .default; - }, get Modal(): Modal { return require('./Libraries/Modal/Modal'); }, diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 70efb104d59177..aae8bc3f91dfb1 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,6 +1,6 @@ { "name": "react-native", - "version": "1000.0.0", + "version": "0.74.2", "description": "A framework for building native apps using React", "license": "MIT", "repository": { @@ -64,6 +64,9 @@ "scripts/hermes/hermes-utils.js", "scripts/hermes/prepare-hermes-for-build.js", "scripts/ios-configure-glog.sh", + "scripts/xcode/ccache-clang++.sh", + "scripts/xcode/ccache-clang.sh", + "scripts/xcode/ccache.conf", "scripts/xcode/with-environment.sh", "scripts/native_modules.rb", "scripts/node-binary.sh", @@ -94,20 +97,26 @@ "featureflags-update": "node ./scripts/featureflags/index.js" }, "peerDependencies": { + "@types/react": "^18.2.6", "react": "18.2.0" }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, "dependencies": { "@jest/create-cache-key-function": "^29.6.3", - "@react-native-community/cli": "13.6.0", - "@react-native-community/cli-platform-android": "13.6.0", - "@react-native-community/cli-platform-ios": "13.6.0", - "@react-native/assets-registry": "0.74.0", - "@react-native/codegen": "0.74.0", - "@react-native/community-cli-plugin": "0.74.0", - "@react-native/gradle-plugin": "0.74.0", - "@react-native/js-polyfills": "0.74.0", - "@react-native/normalize-colors": "0.74.1", - "@react-native/virtualized-lists": "0.74.0", + "@react-native-community/cli": "13.6.9", + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-platform-ios": "13.6.9", + "@react-native/assets-registry": "0.74.84", + "@react-native/codegen": "0.74.84", + "@react-native/community-cli-plugin": "0.74.84", + "@react-native/gradle-plugin": "0.74.84", + "@react-native/js-polyfills": "0.74.84", + "@react-native/normalize-colors": "0.74.84", + "@react-native/virtualized-lists": "0.74.84", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -153,4 +162,4 @@ } ] } -} +} \ No newline at end of file diff --git a/packages/react-native/scripts/cocoapods/helpers.rb b/packages/react-native/scripts/cocoapods/helpers.rb index 1e115ef1755528..68017d1f0cc535 100644 --- a/packages/react-native/scripts/cocoapods/helpers.rb +++ b/packages/react-native/scripts/cocoapods/helpers.rb @@ -41,6 +41,10 @@ def self.min_ios_version_supported return '13.4' end + def self.min_xcode_version_supported + return '14.3' + end + def self.folly_config return { :version => '2024.01.01.00', diff --git a/packages/react-native/scripts/cocoapods/privacy_manifest_utils.rb b/packages/react-native/scripts/cocoapods/privacy_manifest_utils.rb new file mode 100644 index 00000000000000..42ce104571af1d --- /dev/null +++ b/packages/react-native/scripts/cocoapods/privacy_manifest_utils.rb @@ -0,0 +1,173 @@ +# 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. + +module PrivacyManifestUtils + def self.add_aggregated_privacy_manifest(installer) + user_project = get_user_project_from(installer) + targets = get_application_targets(user_project) + file_path = get_privacyinfo_file_path(user_project, targets) + + privacy_info = read_privacyinfo_file(file_path) || { + "NSPrivacyCollectedDataTypes" => [], + "NSPrivacyTracking" => false + } + + # Get all required reason APIs defined in current pods + required_reason_apis = get_used_required_reason_apis(installer) + + # Add the Required Reason APIs from React Native core + get_core_accessed_apis.each do |accessed_api| + api_type = accessed_api["NSPrivacyAccessedAPIType"] + reasons = accessed_api["NSPrivacyAccessedAPITypeReasons"] + required_reason_apis[api_type] ||= [] + required_reason_apis[api_type] += reasons + end + + # Merge the Required Reason APIs from pods with the ones from the existing PrivacyInfo file + (privacy_info["NSPrivacyAccessedAPITypes"] || []).each do |accessed_api| + api_type = accessed_api["NSPrivacyAccessedAPIType"] + reasons = accessed_api["NSPrivacyAccessedAPITypeReasons"] + # Add reasons from existing PrivacyInfo file to the ones from pods + required_reason_apis[api_type] ||= [] + required_reason_apis[api_type] += reasons + end + + # Update the existing PrivacyInfo file with the new aggregated data + privacy_info["NSPrivacyAccessedAPITypes"] = required_reason_apis.map { |api_type, reasons| + { + "NSPrivacyAccessedAPIType" => api_type, + "NSPrivacyAccessedAPITypeReasons" => reasons.uniq + } + } + + Xcodeproj::Plist.write_to_path(privacy_info, file_path) + + targets.each do |target| + ensure_reference(file_path, user_project, target) + end + end + + def self.get_application_targets(user_project) + return user_project.targets.filter { |t| t.symbol_type == :application } + end + + def self.read_privacyinfo_file(file_path) + # Maybe add missing default NSPrivacyTracking, NSPrivacyTrackingDomains, NSPrivacyCollectedDataTypes, but this works without those keys + source_data = nil + # Try to read an existing PrivacyInfo.xcprivacy file + begin + source_data = Xcodeproj::Plist.read_from_path(file_path) + Pod::UI.puts "[Privacy Manifest Aggregation] Appending aggregated reasons to existing PrivacyInfo.xcprivacy file." + rescue => e + Pod::UI.puts "[Privacy Manifest Aggregation] No existing PrivacyInfo.xcprivacy file found, creating a new one." + end + return source_data + end + + def self.ensure_reference(file_path, user_project, target) + reference_exists = target.resources_build_phase.files_references.any? { |file_ref| file_ref.path&.end_with? "PrivacyInfo.xcprivacy" } + unless reference_exists + # We try to find the main group, but if it doesn't exist, we default to adding the file to the project root – both work + file_root = user_project.root_object.main_group.children.find { |group| + group.class == Xcodeproj::Project::Object::PBXGroup && (group.name == target.name || group.path == target.name) + } || user_project + file_ref = file_root.new_file(file_path) + build_file = target.resources_build_phase.add_file_reference(file_ref, true) + end + end + + def self.get_privacyinfo_file_path(user_project, targets) + file_refs = targets.flat_map { |target| target.resources_build_phase.files_references } + existing_file = file_refs.find { |file_ref| file_ref.path&.end_with? "PrivacyInfo.xcprivacy" } + if existing_file + return existing_file.real_path + end + + # We try to find a file we know exists in the project to get the path to the main group directory + info_plist_path = user_project.files.find { |file_ref| file_ref.name == "Info.plist" } + if info_plist_path.nil? + # return path that is sibling to .xcodeproj + path = user_project.path + return File.join(File.dirname(path), "PrivacyInfo.xcprivacy") + end + return File.join(File.dirname(info_plist_path.real_path),"PrivacyInfo.xcprivacy") + end + + def self.get_used_required_reason_apis(installer) + # A dictionary with keys of type string (NSPrivacyAccessedAPIType) and values of type string[] (NSPrivacyAccessedAPITypeReasons[]) + used_apis = {} + Pod::UI.puts "[Privacy Manifest Aggregation] Reading .xcprivacy files to aggregate all used Required Reason APIs." + installer.pod_targets.each do |pod_target| + # puts pod_target + pod_target.file_accessors.each do |file_accessor| + file_accessor.resource_bundles.each do |bundle_name, bundle_files| + bundle_files.each do |file_path| + # This needs to be named like that due to apple requirements + if File.basename(file_path) == 'PrivacyInfo.xcprivacy' + content = Xcodeproj::Plist.read_from_path(file_path) + accessed_api_types = content["NSPrivacyAccessedAPITypes"] + accessed_api_types&.each do |accessed_api| + api_type = accessed_api["NSPrivacyAccessedAPIType"] + reasons = accessed_api["NSPrivacyAccessedAPITypeReasons"] + next unless api_type + used_apis[api_type] ||= [] + used_apis[api_type] += reasons + end + end + end + end + end + end + return used_apis + end + + def self.get_privacy_manifest_paths_from(user_project) + privacy_manifests = user_project + .files + .select { |p| + p.path&.end_with?('PrivacyInfo.xcprivacy') + } + return privacy_manifests + end + + def self.get_core_accessed_apis() + file_timestamp_accessed_api = { + "NSPrivacyAccessedAPIType" => "NSPrivacyAccessedAPICategoryFileTimestamp", + "NSPrivacyAccessedAPITypeReasons" => ["C617.1"], + } + user_defaults_accessed_api = { + "NSPrivacyAccessedAPIType" => "NSPrivacyAccessedAPICategoryUserDefaults", + "NSPrivacyAccessedAPITypeReasons" => ["CA92.1"], + } + boot_time_accessed_api = { + "NSPrivacyAccessedAPIType" => "NSPrivacyAccessedAPICategorySystemBootTime", + "NSPrivacyAccessedAPITypeReasons" => ["35F9.1"], + } + return [file_timestamp_accessed_api, user_defaults_accessed_api, boot_time_accessed_api] + end + + + def self.get_user_project_from(installer) + user_project = installer.aggregate_targets + .map{ |t| t.user_project } + .first + return user_project + end + + def self.add_privacy_manifest_if_needed(installer) + user_project = get_user_project_from(installer) + privacy_manifest = self.get_privacy_manifest_paths_from(user_project).first + if privacy_manifest.nil? + privacy_manifest = { + "NSPrivacyCollectedDataTypes" => [], + "NSPrivacyTracking" => false, + "NSPrivacyAccessedAPITypes" => get_core_accessed_apis + } + path = File.join(user_project.path.parent, "PrivacyInfo.xcprivacy") + Xcodeproj::Plist.write_to_path(privacy_manifest, path) + Pod::UI.puts "Your app does not have a privacy manifest! A template has been generated containing Required Reasons API usage in the core React Native library. Please add the PrivacyInfo.xcprivacy file to your project and complete data use, tracking and any additional required reasons your app is using according to Apple's guidance: https://developer.apple.com/documentation/bundleresources/privacy_manifest_files. Then, you will need to manually add this file to your project in Xcode.".red + end + end +end diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 6df005cee4fc1e..2143df11f0a9d9 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -44,6 +44,7 @@ def self.has_pod(installer, name) def self.set_gcc_preprocessor_definition_for_React_hermes(installer) self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-hermes", "Debug") self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "hermes-engine", "Debug") + self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-RuntimeHermes", "Debug") end def self.turn_off_resource_bundle_react_core(installer) @@ -98,11 +99,12 @@ def self.set_ccache_compiler_and_linker_build_settings(installer, react_native_p Pod::UI.puts("#{message_prefix}: Ccache found at #{ccache_path}") end + # Using scripts wrapping the ccache executable, to allow injection of configurations + ccache_clang_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang.sh') + ccache_clangpp_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang++.sh') + if ccache_available and ccache_enabled Pod::UI.puts("#{message_prefix}: Setting CC, LD, CXX & LDPLUSPLUS build settings") - # Using scripts wrapping the ccache executable, to allow injection of configurations - ccache_clang_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang.sh') - ccache_clangpp_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang++.sh') projects.each do |project| project.build_configurations.each do |config| @@ -119,6 +121,20 @@ def self.set_ccache_compiler_and_linker_build_settings(installer, react_native_p Pod::UI.puts("#{message_prefix}: Pass ':ccache_enabled => true' to 'react_native_post_install' in your Podfile or set environment variable 'USE_CCACHE=1' to increase the speed of subsequent builds") elsif !ccache_available and ccache_enabled Pod::UI.warn("#{message_prefix}: Install ccache or ensure your neither passing ':ccache_enabled => true' nor setting environment variable 'USE_CCACHE=1'") + else + Pod::UI.puts("#{message_prefix}: Removing Ccache from CC, LD, CXX & LDPLUSPLUS build settings") + + projects.each do |project| + project.build_configurations.each do |config| + # Using the un-qualified names means you can swap in different implementations, for example ccache + config.build_settings["CC"] = config.build_settings["CC"] ? config.build_settings["CC"].gsub(/#{Regexp.escape(ccache_clang_sh)}/, '') : "" + config.build_settings["LD"] = config.build_settings["LD"] ? config.build_settings["LD"].gsub(/#{Regexp.escape(ccache_clang_sh)}/, "") : "" + config.build_settings["CXX"] = config.build_settings["CXX"] ? config.build_settings["CXX"].gsub(/#{Regexp.escape(ccache_clangpp_sh)}/, "") : "" + config.build_settings["LDPLUSPLUS"] = config.build_settings["LDPLUSPLUS"] ? config.build_settings["LDPLUSPLUS"].gsub(/#{Regexp.escape(ccache_clangpp_sh)}/, "") : "" + end + + project.save() + end end end @@ -189,7 +205,7 @@ def self.add_build_settings_to_pod(installer, settings_name, settings_value, tar installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result| if pod_name.to_s == target_pod_name target_installation_result.native_target.build_configurations.each do |config| - if configuration == nil || (configuration != nil && configuration == config.name) + if configuration == nil || (configuration != nil && config.name.include?(configuration)) config.build_settings[settings_name] ||= '$(inherited) ' config.build_settings[settings_name] << settings_value end @@ -407,19 +423,39 @@ def self.remove_value_from_setting_if_present(config, setting_name, value) def self.is_using_xcode15_0(xcodebuild_manager: Xcodebuild) xcodebuild_version = xcodebuild_manager.version + if version = self.parse_xcode_version(xcodebuild_version) + return version["major"] == 15 && version["minor"] == 0 + end + + return false + end + + def self.parse_xcode_version(version_string) # The output of xcodebuild -version is something like # Xcode 15.0 # or # Xcode 14.3.1 # We want to capture the version digits - regex = /(\d+)\.(\d+)(?:\.(\d+))?/ - if match_data = xcodebuild_version.match(regex) - major = match_data[1].to_i - minor = match_data[2].to_i - return major == 15 && minor == 0 + match = version_string.match(/(\d+)\.(\d+)(?:\.(\d+))?/) + return nil if match.nil? + + return {"str" => match[0], "major" => match[1].to_i, "minor" => match[2].to_i}; + end + + def self.check_minimum_required_xcode(xcodebuild_manager: Xcodebuild) + version = self.parse_xcode_version(xcodebuild_manager.version) + if (version.nil? || !Gem::Version::correct?(version["str"])) + Pod::UI.warn "Unexpected XCode version string '#{xcodebuild_manager.version}'" + return end - return false + current = version["str"] + min_required = Helpers::Constants.min_xcode_version_supported + + if Gem::Version::new(current) < Gem::Version::new(min_required) + Pod::UI.puts "React Native requires XCode >= #{min_required}. Found #{current}.".red + raise "Please upgrade XCode" + end end def self.add_compiler_flag_to_project(installer, flag, configuration: nil) diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor.js b/packages/react-native/scripts/codegen/generate-artifacts-executor.js index 92434047d5a6a5..c150b1956a648e 100644 --- a/packages/react-native/scripts/codegen/generate-artifacts-executor.js +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor.js @@ -283,11 +283,7 @@ function findLibrariesFromReactNativeConfig(projectRoot) { return []; } - return extractLibrariesFromJSON( - configFile, - configFile.name, - codegenConfigFileDir, - ); + return extractLibrariesFromJSON(configFile, codegenConfigFileDir); }); } diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index ef91f0f6e352c5..3f100dee8921a8 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -18,6 +18,21 @@ const definitions: FeatureFlagDefinitions = { defaultValue: false, }, + androidEnablePendingFabricTransactions: { + defaultValue: false, + description: + "To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically", + }, + batchRenderingUpdatesInEventLoop: { + defaultValue: false, + description: + 'When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop.', + }, + destroyFabricSurfacesInReactInstanceManager: { + defaultValue: false, + description: + 'When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy().', + }, enableBackgroundExecutor: { description: 'Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used).', @@ -33,11 +48,6 @@ const definitions: FeatureFlagDefinitions = { 'Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution).', defaultValue: false, }, - batchRenderingUpdatesInEventLoop: { - description: - 'When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop.', - defaultValue: false, - }, enableSpannableBuildingUnification: { description: 'Uses new, deduplicated logic for constructing Android Spannables from text fragments', diff --git a/packages/react-native/scripts/ios-configure-glog.sh b/packages/react-native/scripts/ios-configure-glog.sh index f13c61b40e229a..02a875d31e408a 100755 --- a/packages/react-native/scripts/ios-configure-glog.sh +++ b/packages/react-native/scripts/ios-configure-glog.sh @@ -42,8 +42,15 @@ EOF patch -p1 config.sub fix_glog_0.3.5_apple_silicon.patch fi -export CC="$(xcrun -find -sdk $PLATFORM_NAME cc) -arch $CURRENT_ARCH -isysroot $(xcrun -sdk $PLATFORM_NAME --show-sdk-path)" -export CXX="$CC" +XCRUN="$(which xcrun)" +if [ -n "$XCRUN" ]; then + export CC="$(xcrun -find -sdk $PLATFORM_NAME cc) -arch $CURRENT_ARCH -isysroot $(xcrun -sdk $PLATFORM_NAME --show-sdk-path)" + export CXX="$CC" +else + export CC="$CC:-$(which gcc)" + export CXX="$CXX:-$(which g++ || true)" +fi +export CXX="$CXX:-$CC" # Remove automake symlink if it exists if [ -h "test-driver" ]; then diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 0943e9029e9dc5..7a8ed1836aed97 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -16,6 +16,7 @@ require_relative './cocoapods/local_podspec_patch.rb' require_relative './cocoapods/runtime.rb' require_relative './cocoapods/helpers.rb' +require_relative './cocoapods/privacy_manifest_utils.rb' $CODEGEN_OUTPUT_DIR = 'build/generated/ios' $CODEGEN_COMPONENT_DIR = 'react/renderer/components' @@ -73,13 +74,16 @@ def use_react_native! ( production: false, # deprecated hermes_enabled: ENV['USE_HERMES'] && ENV['USE_HERMES'] == '0' ? false : true, app_path: '..', - config_file_dir: '' + config_file_dir: '', + privacy_file_aggregation_enabled: true ) # Set the app_path as env variable so the podspecs can access it. ENV['APP_PATH'] = app_path ENV['REACT_NATIVE_PATH'] = path + ReactNativePodsUtils.check_minimum_required_xcode() + # Current target definition is provided by Cocoapods and it refers to the target # that has invoked the `use_react_native!` function. ReactNativePodsUtils.detect_use_frameworks(current_target_definition) @@ -95,6 +99,7 @@ def use_react_native! ( ENV['RCT_FABRIC_ENABLED'] = fabric_enabled ? "1" : "0" ENV['USE_HERMES'] = hermes_enabled ? "1" : "0" + ENV['RCT_AGGREGATE_PRIVACY_FILES'] = privacy_file_aggregation_enabled ? "1" : "0" prefix = path @@ -276,6 +281,7 @@ def react_native_post_install( fabric_enabled = ENV['RCT_FABRIC_ENABLED'] == '1' hermes_enabled = ENV['USE_HERMES'] == '1' + privacy_file_aggregation_enabled = ENV['RCT_AGGREGATE_PRIVACY_FILES'] == '1' if hermes_enabled ReactNativePodsUtils.set_gcc_preprocessor_definition_for_React_hermes(installer) @@ -286,11 +292,19 @@ def react_native_post_install( ReactNativePodsUtils.set_use_hermes_build_setting(installer, hermes_enabled) ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path) ReactNativePodsUtils.set_ccache_compiler_and_linker_build_settings(installer, react_native_path, ccache_enabled) - ReactNativePodsUtils.apply_xcode_15_patch(installer) + if Environment.new().ruby_platform().include?('darwin') + ReactNativePodsUtils.apply_xcode_15_patch(installer) + end ReactNativePodsUtils.updateOSDeploymentTarget(installer) ReactNativePodsUtils.set_dynamic_frameworks_flags(installer) ReactNativePodsUtils.add_ndebug_flag_to_pods_in_release(installer) + if privacy_file_aggregation_enabled + PrivacyManifestUtils.add_aggregated_privacy_manifest(installer) + else + PrivacyManifestUtils.add_privacy_manifest_if_needed(installer) + end + NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer) NewArchitectureHelper.modify_flags_for_new_architecture(installer, NewArchitectureHelper.new_arch_enabled) diff --git a/packages/react-native/sdks/.hermesversion b/packages/react-native/sdks/.hermesversion new file mode 100644 index 00000000000000..038c63a3fd7afc --- /dev/null +++ b/packages/react-native/sdks/.hermesversion @@ -0,0 +1 @@ +hermes-2024-06-03-RNv0.74.2-bb1e74fe1e95c2b5a2f4f9311152da052badc2bc diff --git a/packages/react-native/sdks/hermes-engine/hermes-utils.rb b/packages/react-native/sdks/hermes-engine/hermes-utils.rb index 8c2f4f6c665a83..c0c7613192ebd8 100644 --- a/packages/react-native/sdks/hermes-engine/hermes-utils.rb +++ b/packages/react-native/sdks/hermes-engine/hermes-utils.rb @@ -7,6 +7,7 @@ require 'rexml/document' HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git" +ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE" module HermesEngineSourceType LOCAL_PREBUILT_TARBALL = :local_prebuilt_tarball @@ -30,7 +31,7 @@ def HermesEngineSourceType.isFromSource(source_type) # - To use a specific tarball, install the dependencies with: # `HERMES_ENGINE_TARBALL_PATH= bundle exec pod install` # - To force a build from source, install the dependencies with: -# `BUILD_FROM_SOURCE=true bundle exec pod install` +# `RCT_BUILD_HERMES_FROM_SOURCE=true bundle exec pod install` # If none of the two are provided, Cocoapods will check whether there is a tarball for the current version # (either release or nightly). If not, it will fall back to building from source (the latest commit on main). # @@ -85,11 +86,11 @@ def hermes_commit_envvar_defined() end def force_build_from_tag(react_native_path) - return ENV['BUILD_FROM_SOURCE'] === 'true' && File.exist?(hermestag_file(react_native_path)) + return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && File.exist?(hermestag_file(react_native_path)) end def force_build_from_main(react_native_path) - return ENV['BUILD_FROM_SOURCE'] === 'true' && !File.exist?(hermestag_file(react_native_path)) + return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && !File.exist?(hermestag_file(react_native_path)) end def release_artifact_exists(version) diff --git a/packages/react-native/sdks/hermes-engine/utils/build-ios-framework.sh b/packages/react-native/sdks/hermes-engine/utils/build-ios-framework.sh index a4026a7a948310..8f598166036ff3 100755 --- a/packages/react-native/sdks/hermes-engine/utils/build-ios-framework.sh +++ b/packages/react-native/sdks/hermes-engine/utils/build-ios-framework.sh @@ -4,6 +4,11 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +if [ "$CI" ]; then + set -x +fi +set -e + # Given a specific target, retrieve the right architecture for it # $1 the target you want to build. Allowed values: iphoneos, iphonesimulator, catalyst function get_architecture { diff --git a/packages/react-native/sdks/hermes-engine/utils/build-mac-framework.sh b/packages/react-native/sdks/hermes-engine/utils/build-mac-framework.sh index 0492ad28f22eea..5abac8e3604a1d 100755 --- a/packages/react-native/sdks/hermes-engine/utils/build-mac-framework.sh +++ b/packages/react-native/sdks/hermes-engine/utils/build-mac-framework.sh @@ -4,6 +4,11 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +if [ "$CI" ]; then + set -x +fi +set -e + # shellcheck source=xplat/js/react-native-github/sdks/hermes-engine/utils/build-apple-framework.sh CURR_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" . "${CURR_SCRIPT_DIR}/build-apple-framework.sh" diff --git a/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js index 93243673eba9d2..0aa146d124e170 100644 --- a/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8509d5ee87efb5aa8da7efcd2085d0a2>> + * @generated SignedSource<<8f82962343a5146622f36c2de071ff6a>> * @flow strict-local */ @@ -24,10 +24,12 @@ import * as TurboModuleRegistry from '../../../Libraries/TurboModule/TurboModule export interface Spec extends TurboModule { +commonTestFlag?: () => boolean; + +androidEnablePendingFabricTransactions?: () => boolean; + +batchRenderingUpdatesInEventLoop?: () => boolean; + +destroyFabricSurfacesInReactInstanceManager?: () => boolean; +enableBackgroundExecutor?: () => boolean; +useModernRuntimeScheduler?: () => boolean; +enableMicrotasks?: () => boolean; - +batchRenderingUpdatesInEventLoop?: () => boolean; +enableSpannableBuildingUnification?: () => boolean; +enableCustomDrawOrderFabric?: () => boolean; +enableFixForClippedSubviewsCrash?: () => boolean; diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 214dc80c030845..117ea39d7f0c71 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7c83d5613c3be517efe48378e6356e79>> + * @generated SignedSource<> * @flow strict-local */ @@ -41,10 +41,12 @@ export type ReactNativeFeatureFlagsJsOnlyOverrides = Partial, + androidEnablePendingFabricTransactions: Getter, + batchRenderingUpdatesInEventLoop: Getter, + destroyFabricSurfacesInReactInstanceManager: Getter, enableBackgroundExecutor: Getter, useModernRuntimeScheduler: Getter, enableMicrotasks: Getter, - batchRenderingUpdatesInEventLoop: Getter, enableSpannableBuildingUnification: Getter, enableCustomDrawOrderFabric: Getter, enableFixForClippedSubviewsCrash: Getter, @@ -96,6 +98,18 @@ export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter = cre * Common flag for testing. Do NOT modify. */ export const commonTestFlag: Getter = createNativeFlagGetter('commonTestFlag', false); +/** + * To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically + */ +export const androidEnablePendingFabricTransactions: Getter = createNativeFlagGetter('androidEnablePendingFabricTransactions', false); +/** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ +export const batchRenderingUpdatesInEventLoop: Getter = createNativeFlagGetter('batchRenderingUpdatesInEventLoop', false); +/** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ +export const destroyFabricSurfacesInReactInstanceManager: Getter = createNativeFlagGetter('destroyFabricSurfacesInReactInstanceManager', false); /** * Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used). */ @@ -108,10 +122,6 @@ export const useModernRuntimeScheduler: Getter = createNativeFlagGetter * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). */ export const enableMicrotasks: Getter = createNativeFlagGetter('enableMicrotasks', false); -/** - * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. - */ -export const batchRenderingUpdatesInEventLoop: Getter = createNativeFlagGetter('batchRenderingUpdatesInEventLoop', false); /** * Uses new, deduplicated logic for constructing Android Spannables from text fragments */ diff --git a/packages/react-native/template/android/gradle-wrapper.jar b/packages/react-native/template/android/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1..00000000000000 Binary files a/packages/react-native/template/android/gradle-wrapper.jar and /dev/null differ diff --git a/packages/react-native/template/android/gradle-wrapper.properties b/packages/react-native/template/android/gradle-wrapper.properties deleted file mode 100644 index e6aba2515d5428..00000000000000 --- a/packages/react-native/template/android/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/react-native/template/ios/HelloWorld.xcodeproj/project.pbxproj b/packages/react-native/template/ios/HelloWorld.xcodeproj/project.pbxproj index d059f8e4f5fb7c..40b32ffd09ec6c 100644 --- a/packages/react-native/template/ios/HelloWorld.xcodeproj/project.pbxproj +++ b/packages/react-native/template/ios/HelloWorld.xcodeproj/project.pbxproj @@ -36,6 +36,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HelloWorld/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HelloWorld/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = HelloWorld/main.m; sourceTree = ""; }; + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = HelloWorld/PrivacyInfo.xcprivacy; sourceTree = ""; }; 19F6CBCC0A4E27FBF8BF4A61 /* libPods-HelloWorld-HelloWorldTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld-HelloWorldTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = ""; }; 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = ""; }; @@ -92,6 +93,7 @@ 13B07FB61A68108700A75B9A /* Info.plist */, 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, 13B07FB71A68108700A75B9A /* main.m */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, ); name = HelloWorld; sourceTree = ""; diff --git a/packages/react-native/template/ios/HelloWorld/PrivacyInfo.xcprivacy b/packages/react-native/template/ios/HelloWorld/PrivacyInfo.xcprivacy new file mode 100644 index 00000000000000..ef1896e70c88da --- /dev/null +++ b/packages/react-native/template/ios/HelloWorld/PrivacyInfo.xcprivacy @@ -0,0 +1,38 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyTracking + + + diff --git a/packages/react-native/template/package.json b/packages/react-native/template/package.json index 232831747e774e..70b2b0b1a42684 100644 --- a/packages/react-native/template/package.json +++ b/packages/react-native/template/package.json @@ -11,16 +11,16 @@ }, "dependencies": { "react": "18.2.0", - "react-native": "1000.0.0" + "react-native": "0.74.2" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", - "@react-native/babel-preset": "0.74.0", - "@react-native/eslint-config": "0.74.0", - "@react-native/metro-config": "0.74.0", - "@react-native/typescript-config": "0.74.0", + "@react-native/babel-preset": "0.74.84", + "@react-native/eslint-config": "0.74.84", + "@react-native/metro-config": "0.74.84", + "@react-native/typescript-config": "0.74.84", "@types/react": "^18.2.6", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.6.3", diff --git a/packages/react-native/third-party-podspecs/RCT-Folly.podspec b/packages/react-native/third-party-podspecs/RCT-Folly.podspec index eb2d9b4ed8cb85..ccc10a5b47ccef 100644 --- a/packages/react-native/third-party-podspecs/RCT-Folly.podspec +++ b/packages/react-native/third-party-podspecs/RCT-Folly.podspec @@ -81,6 +81,7 @@ Pod::Spec.new do |spec| 'folly/system/*.h', spec.libraries = "c++abi" # NOTE Apple-only: Keep c++abi here due to https://github.com/react-native-community/releases/issues/251 spec.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", + "DEFINES_MODULE" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fmt/include\"", # In dynamic framework (use_frameworks!) mode, ignore the unused and undefined boost symbols when generating the library. diff --git a/packages/react-native/types/index.d.ts b/packages/react-native/types/index.d.ts index b23e2e0a2ff267..5aeec0685ed009 100644 --- a/packages/react-native/types/index.d.ts +++ b/packages/react-native/types/index.d.ts @@ -103,6 +103,7 @@ export * from '../Libraries/Components/View/View'; export * from '../Libraries/Components/View/ViewAccessibility'; export * from '../Libraries/Components/View/ViewPropTypes'; export * from '../Libraries/Components/Button'; +export * from '../Libraries/Core/registerCallableModule'; export * from '../Libraries/DevToolsSettings/DevToolsSettingsManager'; export * from '../Libraries/EventEmitter/NativeEventEmitter'; export * from '../Libraries/EventEmitter/RCTDeviceEventEmitter'; diff --git a/packages/rn-tester-e2e/package.json b/packages/rn-tester-e2e/package.json index ef06a7f8fe1b87..6aff87fefec097 100644 --- a/packages/rn-tester-e2e/package.json +++ b/packages/rn-tester-e2e/package.json @@ -1,7 +1,7 @@ { "name": "@react-native/tester-e2e", "private": true, - "version": "0.0.1", + "version": "0.74.84", "license": "MIT", "description": "React Native E2E tester app.", "homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/rn-tester-e2e", diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 66e57f4f87b2d6..719fc010232ce6 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -1,19 +1,13 @@ PODS: - boost (1.83.0) - DoubleConversion (1.1.6) - - FBLazyVector (1000.0.0) + - FBLazyVector (0.74.2) - fmt (9.1.0) - glog (0.3.5) - - hermes-engine (1000.0.0): - - hermes-engine/Hermes (= 1000.0.0) - - hermes-engine/inspector (= 1000.0.0) - - hermes-engine/inspector_chrome (= 1000.0.0) - - hermes-engine/Public (= 1000.0.0) - - hermes-engine/Hermes (1000.0.0) - - hermes-engine/inspector (1000.0.0) - - hermes-engine/inspector_chrome (1000.0.0) - - hermes-engine/Public (1000.0.0) - - MyNativeView (0.0.1): + - hermes-engine (0.74.2): + - hermes-engine/Pre-built (= 0.74.2) + - hermes-engine/Pre-built (0.74.2) + - MyNativeView (0.74.84): - DoubleConversion - glog - hermes-engine @@ -34,7 +28,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - NativeCxxModuleExample (0.0.1): + - NativeCxxModuleExample (0.74.84): - DoubleConversion - glog - hermes-engine @@ -72,27 +66,27 @@ PODS: - DoubleConversion - fmt (= 9.1.0) - glog - - RCTDeprecation (1000.0.0) - - RCTRequired (1000.0.0) - - RCTTypeSafety (1000.0.0): - - FBLazyVector (= 1000.0.0) - - RCTRequired (= 1000.0.0) - - React-Core (= 1000.0.0) - - React (1000.0.0): - - React-Core (= 1000.0.0) - - React-Core/DevSupport (= 1000.0.0) - - React-Core/RCTWebSocket (= 1000.0.0) - - React-RCTActionSheet (= 1000.0.0) - - React-RCTAnimation (= 1000.0.0) - - React-RCTBlob (= 1000.0.0) - - React-RCTImage (= 1000.0.0) - - React-RCTLinking (= 1000.0.0) - - React-RCTNetwork (= 1000.0.0) - - React-RCTSettings (= 1000.0.0) - - React-RCTText (= 1000.0.0) - - React-RCTVibration (= 1000.0.0) - - React-callinvoker (1000.0.0) - - React-Codegen (1000.0.0): + - RCTDeprecation (0.74.2) + - RCTRequired (0.74.2) + - RCTTypeSafety (0.74.2): + - FBLazyVector (= 0.74.2) + - RCTRequired (= 0.74.2) + - React-Core (= 0.74.2) + - React (0.74.2): + - React-Core (= 0.74.2) + - React-Core/DevSupport (= 0.74.2) + - React-Core/RCTWebSocket (= 0.74.2) + - React-RCTActionSheet (= 0.74.2) + - React-RCTAnimation (= 0.74.2) + - React-RCTBlob (= 0.74.2) + - React-RCTImage (= 0.74.2) + - React-RCTLinking (= 0.74.2) + - React-RCTNetwork (= 0.74.2) + - React-RCTSettings (= 0.74.2) + - React-RCTText (= 0.74.2) + - React-RCTVibration (= 0.74.2) + - React-callinvoker (0.74.2) + - React-Codegen (0.74.2): - DoubleConversion - glog - hermes-engine @@ -112,12 +106,12 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-Core (1000.0.0): + - React-Core (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 1000.0.0) + - React-Core/Default (= 0.74.2) - React-cxxreact - React-featureflags - React-hermes @@ -129,7 +123,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/CoreModulesHeaders (1000.0.0): + - React-Core/CoreModulesHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -146,7 +140,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/Default (1000.0.0): + - React-Core/Default (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -162,13 +156,13 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/DevSupport (1000.0.0): + - React-Core/DevSupport (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 1000.0.0) - - React-Core/RCTWebSocket (= 1000.0.0) + - React-Core/Default (= 0.74.2) + - React-Core/RCTWebSocket (= 0.74.2) - React-cxxreact - React-featureflags - React-hermes @@ -180,7 +174,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTActionSheetHeaders (1000.0.0): + - React-Core/RCTActionSheetHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -197,7 +191,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTAnimationHeaders (1000.0.0): + - React-Core/RCTAnimationHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -214,7 +208,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTBlobHeaders (1000.0.0): + - React-Core/RCTBlobHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -231,7 +225,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTImageHeaders (1000.0.0): + - React-Core/RCTImageHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -248,7 +242,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTLinkingHeaders (1000.0.0): + - React-Core/RCTLinkingHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -265,7 +259,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTNetworkHeaders (1000.0.0): + - React-Core/RCTNetworkHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -282,7 +276,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTPushNotificationHeaders (1000.0.0): + - React-Core/RCTPushNotificationHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -299,7 +293,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTSettingsHeaders (1000.0.0): + - React-Core/RCTSettingsHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -316,7 +310,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTTextHeaders (1000.0.0): + - React-Core/RCTTextHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -333,7 +327,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTVibrationHeaders (1000.0.0): + - React-Core/RCTVibrationHeaders (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -350,12 +344,12 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTWebSocket (1000.0.0): + - React-Core/RCTWebSocket (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 1000.0.0) + - React-Core/Default (= 0.74.2) - React-cxxreact - React-featureflags - React-hermes @@ -367,36 +361,36 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-CoreModules (1000.0.0): + - React-CoreModules (0.74.2): - DoubleConversion - fmt (= 9.1.0) - RCT-Folly (= 2024.01.01.00) - - RCTTypeSafety (= 1000.0.0) + - RCTTypeSafety (= 0.74.2) - React-Codegen - - React-Core/CoreModulesHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) + - React-Core/CoreModulesHeaders (= 0.74.2) + - React-jsi (= 0.74.2) - React-jsinspector - React-NativeModulesApple - React-RCTBlob - - React-RCTImage (= 1000.0.0) + - React-RCTImage (= 0.74.2) - ReactCommon - SocketRocket (= 0.7.0) - - React-cxxreact (1000.0.0): + - React-cxxreact (0.74.2): - boost (= 1.83.0) - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 1000.0.0) - - React-debug (= 1000.0.0) - - React-jsi (= 1000.0.0) + - React-callinvoker (= 0.74.2) + - React-debug (= 0.74.2) + - React-jsi (= 0.74.2) - React-jsinspector - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - React-runtimeexecutor (= 1000.0.0) - - React-debug (1000.0.0) - - React-Fabric (1000.0.0): + - React-logger (= 0.74.2) + - React-perflogger (= 0.74.2) + - React-runtimeexecutor (= 0.74.2) + - React-debug (0.74.2) + - React-Fabric (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -407,20 +401,20 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 1000.0.0) - - React-Fabric/attributedstring (= 1000.0.0) - - React-Fabric/componentregistry (= 1000.0.0) - - React-Fabric/componentregistrynative (= 1000.0.0) - - React-Fabric/components (= 1000.0.0) - - React-Fabric/core (= 1000.0.0) - - React-Fabric/imagemanager (= 1000.0.0) - - React-Fabric/leakchecker (= 1000.0.0) - - React-Fabric/mounting (= 1000.0.0) - - React-Fabric/scheduler (= 1000.0.0) - - React-Fabric/telemetry (= 1000.0.0) - - React-Fabric/templateprocessor (= 1000.0.0) - - React-Fabric/textlayoutmanager (= 1000.0.0) - - React-Fabric/uimanager (= 1000.0.0) + - React-Fabric/animations (= 0.74.2) + - React-Fabric/attributedstring (= 0.74.2) + - React-Fabric/componentregistry (= 0.74.2) + - React-Fabric/componentregistrynative (= 0.74.2) + - React-Fabric/components (= 0.74.2) + - React-Fabric/core (= 0.74.2) + - React-Fabric/imagemanager (= 0.74.2) + - React-Fabric/leakchecker (= 0.74.2) + - React-Fabric/mounting (= 0.74.2) + - React-Fabric/scheduler (= 0.74.2) + - React-Fabric/telemetry (= 0.74.2) + - React-Fabric/templateprocessor (= 0.74.2) + - React-Fabric/textlayoutmanager (= 0.74.2) + - React-Fabric/uimanager (= 0.74.2) - React-graphics - React-jsi - React-jsiexecutor @@ -429,7 +423,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/animations (1000.0.0): + - React-Fabric/animations (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -448,7 +442,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (1000.0.0): + - React-Fabric/attributedstring (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -467,7 +461,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (1000.0.0): + - React-Fabric/componentregistry (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -486,7 +480,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (1000.0.0): + - React-Fabric/componentregistrynative (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -505,7 +499,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components (1000.0.0): + - React-Fabric/components (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -516,17 +510,17 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/components/inputaccessory (= 1000.0.0) - - React-Fabric/components/legacyviewmanagerinterop (= 1000.0.0) - - React-Fabric/components/modal (= 1000.0.0) - - React-Fabric/components/rncore (= 1000.0.0) - - React-Fabric/components/root (= 1000.0.0) - - React-Fabric/components/safeareaview (= 1000.0.0) - - React-Fabric/components/scrollview (= 1000.0.0) - - React-Fabric/components/text (= 1000.0.0) - - React-Fabric/components/textinput (= 1000.0.0) - - React-Fabric/components/unimplementedview (= 1000.0.0) - - React-Fabric/components/view (= 1000.0.0) + - React-Fabric/components/inputaccessory (= 0.74.2) + - React-Fabric/components/legacyviewmanagerinterop (= 0.74.2) + - React-Fabric/components/modal (= 0.74.2) + - React-Fabric/components/rncore (= 0.74.2) + - React-Fabric/components/root (= 0.74.2) + - React-Fabric/components/safeareaview (= 0.74.2) + - React-Fabric/components/scrollview (= 0.74.2) + - React-Fabric/components/text (= 0.74.2) + - React-Fabric/components/textinput (= 0.74.2) + - React-Fabric/components/unimplementedview (= 0.74.2) + - React-Fabric/components/view (= 0.74.2) - React-graphics - React-jsi - React-jsiexecutor @@ -535,7 +529,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/inputaccessory (1000.0.0): + - React-Fabric/components/inputaccessory (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -554,7 +548,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (1000.0.0): + - React-Fabric/components/legacyviewmanagerinterop (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -573,7 +567,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/modal (1000.0.0): + - React-Fabric/components/modal (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -592,7 +586,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/rncore (1000.0.0): + - React-Fabric/components/rncore (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -611,7 +605,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/root (1000.0.0): + - React-Fabric/components/root (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -630,7 +624,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/safeareaview (1000.0.0): + - React-Fabric/components/safeareaview (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -649,7 +643,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/scrollview (1000.0.0): + - React-Fabric/components/scrollview (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -668,7 +662,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/text (1000.0.0): + - React-Fabric/components/text (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -687,7 +681,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/textinput (1000.0.0): + - React-Fabric/components/textinput (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -706,7 +700,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/unimplementedview (1000.0.0): + - React-Fabric/components/unimplementedview (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -725,7 +719,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/view (1000.0.0): + - React-Fabric/components/view (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -745,7 +739,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-Fabric/core (1000.0.0): + - React-Fabric/core (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -764,7 +758,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (1000.0.0): + - React-Fabric/imagemanager (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -783,7 +777,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (1000.0.0): + - React-Fabric/leakchecker (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -802,7 +796,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/mounting (1000.0.0): + - React-Fabric/mounting (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -821,7 +815,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/scheduler (1000.0.0): + - React-Fabric/scheduler (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -840,7 +834,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/telemetry (1000.0.0): + - React-Fabric/telemetry (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -859,7 +853,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (1000.0.0): + - React-Fabric/templateprocessor (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -878,7 +872,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/textlayoutmanager (1000.0.0): + - React-Fabric/textlayoutmanager (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -898,7 +892,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager (1000.0.0): + - React-Fabric/uimanager (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog @@ -917,44 +911,45 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-FabricImage (1000.0.0): + - React-FabricImage (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) + - RCTRequired (= 0.74.2) + - RCTTypeSafety (= 0.74.2) - React-Fabric - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 1000.0.0) + - React-jsiexecutor (= 0.74.2) - React-logger - React-rendererdebug - React-utils - ReactCommon - Yoga - - React-featureflags (1000.0.0) - - React-graphics (1000.0.0): + - React-featureflags (0.74.2) + - React-graphics (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - RCT-Folly/Fabric (= 2024.01.01.00) - - React-Core/Default (= 1000.0.0) + - React-Core/Default (= 0.74.2) - React-utils - - React-hermes (1000.0.0): + - React-hermes (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 1000.0.0) + - React-cxxreact (= 0.74.2) - React-jsi - - React-jsiexecutor (= 1000.0.0) - - React-jsinspector (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - React-ImageManager (1000.0.0): + - React-jsiexecutor (= 0.74.2) + - React-jsinspector + - React-perflogger (= 0.74.2) + - React-runtimeexecutor + - React-ImageManager (0.74.2): - glog - RCT-Folly/Fabric - React-Core/Default @@ -963,42 +958,45 @@ PODS: - React-graphics - React-rendererdebug - React-utils - - React-jserrorhandler (1000.0.0): + - React-jserrorhandler (0.74.2): - RCT-Folly/Fabric (= 2024.01.01.00) - React-debug - React-jsi - React-Mapbuffer - - React-jsi (1000.0.0): + - React-jsi (0.74.2): - boost (= 1.83.0) - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-jsiexecutor (1000.0.0): + - React-jsiexecutor (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) + - React-cxxreact (= 0.74.2) + - React-jsi (= 0.74.2) - React-jsinspector - - React-perflogger (= 1000.0.0) - - React-jsinspector (1000.0.0): + - React-perflogger (= 0.74.2) + - React-jsinspector (0.74.2): - DoubleConversion - glog + - hermes-engine - RCT-Folly (= 2024.01.01.00) - React-featureflags - - React-jsitracing (1000.0.0): - React-jsi - - React-logger (1000.0.0): + - React-runtimeexecutor (= 0.74.2) + - React-jsitracing (0.74.2): + - React-jsi + - React-logger (0.74.2): - glog - - React-Mapbuffer (1000.0.0): + - React-Mapbuffer (0.74.2): - glog - React-debug - - React-nativeconfig (1000.0.0) - - React-NativeModulesApple (1000.0.0): + - React-nativeconfig (0.74.2) + - React-NativeModulesApple (0.74.2): - glog - hermes-engine - React-callinvoker @@ -1009,10 +1007,10 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (1000.0.0) - - React-RCTActionSheet (1000.0.0): - - React-Core/RCTActionSheetHeaders (= 1000.0.0) - - React-RCTAnimation (1000.0.0): + - React-perflogger (0.74.2) + - React-RCTActionSheet (0.74.2): + - React-Core/RCTActionSheetHeaders (= 0.74.2) + - React-RCTAnimation (0.74.2): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1020,14 +1018,16 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTAppDelegate (1000.0.0): + - React-RCTAppDelegate (0.74.2): - RCT-Folly (= 2024.01.01.00) - RCTRequired - RCTTypeSafety + - React-Codegen - React-Core - React-CoreModules - React-debug - React-Fabric + - React-featureflags - React-graphics - React-hermes - React-nativeconfig @@ -1042,7 +1042,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon - - React-RCTBlob (1000.0.0): + - React-RCTBlob (0.74.2): - DoubleConversion - fmt (= 9.1.0) - hermes-engine @@ -1055,7 +1055,7 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTFabric (1000.0.0): + - React-RCTFabric (0.74.2): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) @@ -1063,6 +1063,7 @@ PODS: - React-debug - React-Fabric - React-FabricImage + - React-featureflags - React-graphics - React-ImageManager - React-jsi @@ -1074,7 +1075,7 @@ PODS: - React-runtimescheduler - React-utils - Yoga - - React-RCTImage (1000.0.0): + - React-RCTImage (0.74.2): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1083,14 +1084,14 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTLinking (1000.0.0): + - React-RCTLinking (0.74.2): - React-Codegen - - React-Core/RCTLinkingHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) + - React-Core/RCTLinkingHeaders (= 0.74.2) + - React-jsi (= 0.74.2) - React-NativeModulesApple - ReactCommon - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTNetwork (1000.0.0): + - ReactCommon/turbomodule/core (= 0.74.2) + - React-RCTNetwork (0.74.2): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1098,14 +1099,14 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTPushNotification (1000.0.0): + - React-RCTPushNotification (0.74.2): - RCTTypeSafety - React-Codegen - React-Core/RCTPushNotificationHeaders - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTSettings (1000.0.0): + - React-RCTSettings (0.74.2): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1113,29 +1114,29 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTTest (1000.0.0): + - React-RCTTest (0.74.2): - RCT-Folly (= 2024.01.01.00) - - React-Core (= 1000.0.0) - - React-CoreModules (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTText (1000.0.0): - - React-Core/RCTTextHeaders (= 1000.0.0) + - React-Core (= 0.74.2) + - React-CoreModules (= 0.74.2) + - React-jsi (= 0.74.2) + - ReactCommon/turbomodule/core (= 0.74.2) + - React-RCTText (0.74.2): + - React-Core/RCTTextHeaders (= 0.74.2) - Yoga - - React-RCTVibration (1000.0.0): + - React-RCTVibration (0.74.2): - RCT-Folly (= 2024.01.01.00) - React-Codegen - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - ReactCommon - - React-rendererdebug (1000.0.0): + - React-rendererdebug (0.74.2): - DoubleConversion - fmt (= 9.1.0) - RCT-Folly (= 2024.01.01.00) - React-debug - - React-rncore (1000.0.0) - - React-RuntimeApple (1000.0.0): + - React-rncore (0.74.2) + - React-RuntimeApple (0.74.2): - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - React-callinvoker @@ -1153,7 +1154,7 @@ PODS: - React-runtimeexecutor - React-RuntimeHermes - React-utils - - React-RuntimeCore (1000.0.0): + - React-RuntimeCore (0.74.2): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) @@ -1166,18 +1167,20 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - React-runtimeexecutor (1000.0.0): - - React-jsi (= 1000.0.0) - - React-RuntimeHermes (1000.0.0): + - React-runtimeexecutor (0.74.2): + - React-jsi (= 0.74.2) + - React-RuntimeHermes (0.74.2): - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - React-featureflags + - React-hermes - React-jsi + - React-jsinspector - React-jsitracing - React-nativeconfig - React-RuntimeCore - React-utils - - React-runtimescheduler (1000.0.0): + - React-runtimescheduler (0.74.2): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -1189,14 +1192,15 @@ PODS: - React-rendererdebug - React-runtimeexecutor - React-utils - - React-utils (1000.0.0): + - React-utils (0.74.2): - glog + - hermes-engine - RCT-Folly (= 2024.01.01.00) - React-debug - - ReactCommon (1000.0.0): - - React-logger (= 1000.0.0) - - ReactCommon/turbomodule (= 1000.0.0) - - ReactCommon-Samples (1000.0.0): + - React-jsi (= 0.74.2) + - ReactCommon (0.74.2): + - ReactCommon/turbomodule (= 0.74.2) + - ReactCommon-Samples (0.74.2): - DoubleConversion - fmt (= 9.1.0) - hermes-engine @@ -1207,43 +1211,44 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - ReactCommon/turbomodule (1000.0.0): + - ReactCommon/turbomodule (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - ReactCommon/turbomodule/bridging (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - ReactCommon/turbomodule/bridging (1000.0.0): + - React-callinvoker (= 0.74.2) + - React-cxxreact (= 0.74.2) + - React-jsi (= 0.74.2) + - React-logger (= 0.74.2) + - React-perflogger (= 0.74.2) + - ReactCommon/turbomodule/bridging (= 0.74.2) + - ReactCommon/turbomodule/core (= 0.74.2) + - ReactCommon/turbomodule/bridging (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - ReactCommon/turbomodule/core (1000.0.0): + - React-callinvoker (= 0.74.2) + - React-cxxreact (= 0.74.2) + - React-jsi (= 0.74.2) + - React-logger (= 0.74.2) + - React-perflogger (= 0.74.2) + - ReactCommon/turbomodule/core (0.74.2): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-debug (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - ScreenshotManager (0.0.1): + - React-callinvoker (= 0.74.2) + - React-cxxreact (= 0.74.2) + - React-debug (= 0.74.2) + - React-jsi (= 0.74.2) + - React-logger (= 0.74.2) + - React-perflogger (= 0.74.2) + - React-utils (= 0.74.2) + - ScreenshotManager (0.74.84): - DoubleConversion - glog - hermes-engine @@ -1350,7 +1355,7 @@ EXTERNAL SOURCES: :podspec: "../react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: '' + :tag: hermes-2024-06-03-RNv0.74.2-bb1e74fe1e95c2b5a2f4f9311152da052badc2bc MyNativeView: :path: NativeComponentExample NativeCxxModuleExample: @@ -1463,66 +1468,66 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 - FBLazyVector: f4492a543c5a8fa1502d3a5867e3f7252497cfe8 + FBLazyVector: 4bc164e5b5e6cfc288d2b5ff28643ea15fa1a589 fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 - glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 - hermes-engine: b6ed6780e02b97c31d4d84407f579e3523177780 - MyNativeView: 08e59984e995c857953374e22082d058be3cb945 - NativeCxxModuleExample: 51569707f2da9a460d1ebf74a29d02f0a86c549e + glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f + hermes-engine: 01d3e052018c2a13937aca1860fbedbccd4a41b7 + MyNativeView: a13eaef18acc153aecddff297bd2b0d3c616798f + NativeCxxModuleExample: de8e0c86469c4aee4528c8a1a30454d5b7a2caf0 OCMock: 9491e4bec59e0b267d52a9184ff5605995e74be8 - RCT-Folly: 045d6ecaa59d826c5736dfba0b2f4083ff8d79df - RCTDeprecation: 3808e36294137f9ee5668f4df2e73dc079cd1dcf - RCTRequired: 82c56a03b3efd524bfdb581a906add903f78f978 - RCTTypeSafety: 5f57d4ae5dfafc85a0f575d756c909b584722c52 - React: cb6dc75e09f32aeddb4d8fb58a394a67219a92fe - React-callinvoker: bae59cbd6affd712bbfc703839dad868ff35069d - React-Codegen: b66fa3765bc7a786e88e56ff92459a41dec00b0f - React-Core: 738c8db837b21aae813479f2eb284d5507a5c256 - React-CoreModules: 0b94c427e958c1b7ce268d03aa39a1f424d76901 - React-cxxreact: dadc89738f0089c3e2b85f36de940a088759b20d - React-debug: 4097205dd5ff0ada516e7df5b6721a9464a6f20b - React-Fabric: a3214cc064ca25e029aff0a42e2beb8c87b71861 - React-FabricImage: 6daf9bcd047ac1c424cfd7cae298089f8d495761 - React-featureflags: 36f601a77af59dca45877a298c804883dd04f8d0 - React-graphics: 8d54d923bef33e9f423d06c4fd51431f917a4b2f - React-hermes: 14e7007ebbfcc9f674c9c4f3ac768aa587b6da79 - React-ImageManager: 44304168f3ec4733a0191dadee7b1711291272f4 - React-jserrorhandler: 2809ae4d87fb880d2c18be72bcaed114e79f3993 - React-jsi: b7645527d3f77afdea4365488e47dbc5b293177f - React-jsiexecutor: 42eeb6b4e73e1b50caa3940ad0189171723c6b29 - React-jsinspector: 363ee50f69cb39cc2ee4eef0705f2fc4a33f83f3 - React-jsitracing: 46474207a88a3978c08e191d9cf33a6457722d65 - React-logger: 8486d7a1d32b972414b1d34a93470ee2562c6ee2 - React-Mapbuffer: 9d18546228182de42aae8cb047baa5e14886a922 - React-nativeconfig: a5a38eee09c6a57824489bf9005d46d2e3fbb78b - React-NativeModulesApple: ff03b94214a1628f920b43bb64964860fc227f9f - React-perflogger: 70d009f755dd10002183454cdf5ad9b22de4a1d7 - React-RCTActionSheet: 943bd5f540f3af1e5a149c13c4de81858edf718a - React-RCTAnimation: 07583f0ebfa7154f0e696a75c32a8f8b180fc8c5 - React-RCTAppDelegate: 60cfe221df61de818f6e6b200ff55b4346e6ea7c - React-RCTBlob: 2dbf6931deac47ff5d3910e4c81cdd856ea239d6 - React-RCTFabric: e613b7e4aec23114ee6999295ca97b3f0508ee0f - React-RCTImage: 8f46d82257827c2332bc4108fddef1a840f440a7 - React-RCTLinking: efa67827466e50e07c5471447c12e474cbc5e336 - React-RCTNetwork: a80529d2d90f79caa5e31d49e840735a10d6d91a - React-RCTPushNotification: c34ef3969207da3ddc777f36a252f99754b89e2d - React-RCTSettings: 39ca10f68da0ec88a63c33152d43c222c8c38119 - React-RCTTest: 3b9f62c66c3814ccace402441597160aefc9e812 - React-RCTText: d9925903524a7b179cf7803162a98038e0bfb4fd - React-RCTVibration: 33bef249bc4a637ed91bf1cf0d94d9329381dc7b - React-rendererdebug: bc0f2a1816a4607e85ed603170a413c44c1d2635 - React-rncore: 79f594bc32c96203ab607bd9868ec76caa2f290c - React-RuntimeApple: 504b50d20ddf82de9c2c527c6b5a09119e03b737 - React-RuntimeCore: 9dc8d98bc09b0797c2dca7a30be5e8f84ecf5746 - React-runtimeexecutor: e1c32bc249dd3cf3919cb4664fd8dc84ef70cff7 - React-RuntimeHermes: b1f60690c9ed90d448f1325355f72c662c02e49a - React-runtimescheduler: 0df238ee8e88e1b8874d332856ee7d36870be4ad - React-utils: 00c57742056c9c90e58b1a8cd06ca50d30528d6f - ReactCommon: 4148a8bfb8bbfdbea8f517f98cadeeb0f30c8de8 - ReactCommon-Samples: 5d703e2b5e1c8ddb812e7b7cace5a8effbcc6438 - ScreenshotManager: 823189e19d4e6f7d4792fe1db6b037df7efc2fab + RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 + RCTDeprecation: b03c35057846b685b3ccadc9bfe43e349989cdb2 + RCTRequired: 194626909cfa8d39ca6663138c417bc6c431648c + RCTTypeSafety: 552aff5b8e8341660594db00e53ac889682bc120 + React: a57fe42044fe6ed3e828f8867ce070a6c5872754 + React-callinvoker: 6bedefb354a8848b534752417954caa3a5cf34f9 + React-Codegen: 7b38b4f2d723bd0874514582a29526cde4281677 + React-Core: 289ee3dfc1639bb9058c1e77427bb48169c26d7a + React-CoreModules: eda5ce541a1f552158317abd90a5a0f6a4f8d6f7 + React-cxxreact: 56bd17ccc6d4248116f7f95884ddb8c528379fb6 + React-debug: 164b8e302404d92d4bec39778a5e03bcb1b6eb08 + React-Fabric: 05620c36074e3ab397dd8f9db0deb6d3c38b4efa + React-FabricImage: 2a8a7f5729f5c44e32e6f58f7225ee1017ed0704 + React-featureflags: d97a6393993052e951e16a3b81206e22110be8d2 + React-graphics: ef07d701f4eb72ae6fca6ed0a7260a04f2a58dec + React-hermes: 6ccc301ababfa17a9aad25a7e33faf325fd024b4 + React-ImageManager: 00404bfe122626bc6493621f2a31ce802115a9b3 + React-jserrorhandler: 5e2632590a84363855b2083e6b3d501e93bc3f04 + React-jsi: 828703c235f4eea1647897ee8030efdc6e8e9f14 + React-jsiexecutor: 713d7bbef0a410cee5b3b78f73ed1fc16e177ba7 + React-jsinspector: e1fa5325a47f34645195c63e3312ddb6a2efef5d + React-jsitracing: 0fa7f78d8fdda794667cb2e6f19c874c1cf31d7e + React-logger: 29fa3e048f5f67fe396bc08af7606426d9bd7b5d + React-Mapbuffer: bf56147c9775491e53122a94c423ac201417e326 + React-nativeconfig: 9f223cd321823afdecf59ed00861ab2d69ee0fc1 + React-NativeModulesApple: ff7efaff7098639db5631236cfd91d60abff04c0 + React-perflogger: 32ed45d9cee02cf6639acae34251590dccd30994 + React-RCTActionSheet: 19f967ddaea258182b56ef11437133b056ba2adf + React-RCTAnimation: d7f4137fc44a08bba465267ea7cb1dbdb7c4ec87 + React-RCTAppDelegate: 2b3f4d8009796af209a0d496e73276b743acee08 + React-RCTBlob: c6c3e1e0251700b7bea036b893913f22e2b9cb47 + React-RCTFabric: 93a3ea55169d19294f07092013c1c9ea7a015c9b + React-RCTImage: 40528ab74a4fef0f0e2ee797a074b26d120b6cc6 + React-RCTLinking: 385b5beb96749aae9ae1606746e883e1c9f8a6a7 + React-RCTNetwork: ffc9f05bd8fa5b3bce562199ba41235ad0af645c + React-RCTPushNotification: aa71ec3f333746944b29491fddc12d53bf85c1e0 + React-RCTSettings: 21914178bb65cb2c20c655ae1fb401617ae74618 + React-RCTTest: 501b9b159e69009f6c2a5e23d5225f59cea65027 + React-RCTText: 7f8dba1a311e99f4de15bbace2350e805f33f024 + React-RCTVibration: e4ccf673579d0d94a96b3a0b64492db08f8324d5 + React-rendererdebug: ac70f40de137ce7bdbc55eaee60c467a215d9923 + React-rncore: edfff7a3f7f82ca1e0ba26978c6d84c7a8970dac + React-RuntimeApple: a0c98b75571aa5f44ddc7c6e9fd55803fa4db00f + React-RuntimeCore: 4b8db1fe2f3f4a3a5ecb22e1a419824e3e2cd7ef + React-runtimeexecutor: 5961acc7a77b69f964e1645a5d6069e124ce6b37 + React-RuntimeHermes: c5825bfae4815fdf4e9e639340c3a986a491884c + React-runtimescheduler: 56b642bf605ba5afa500d35790928fc1d51565ad + React-utils: 4476b7fcbbd95cfd002f3e778616155241d86e31 + ReactCommon: ecad995f26e0d1e24061f60f4e5d74782f003f12 + ReactCommon-Samples: f00190ee5cf6d6b81fa0482c7100daa5434bc7d2 + ScreenshotManager: f91b842c0f6106e39b7ad5a6ffc092ece82ee778 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: 53e99e2a727b8498ea9e8aed8d917b808c9ff2ea + Yoga: ae3c32c514802d30f687a04a6a35b348506d411f PODFILE CHECKSUM: 60b84dd598fc04e9ed84dbc82e2cb3b99b1d7adf diff --git a/packages/rn-tester/PrivacyInfo.xcprivacy b/packages/rn-tester/PrivacyInfo.xcprivacy new file mode 100644 index 00000000000000..41b8317f0652b9 --- /dev/null +++ b/packages/rn-tester/PrivacyInfo.xcprivacy @@ -0,0 +1,37 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/packages/rn-tester/RNTester/NativeExampleViews/FlexibleSizeExampleView.mm b/packages/rn-tester/RNTester/NativeExampleViews/FlexibleSizeExampleView.mm index 489c3a91e33659..c01c39a4f5a2b6 100644 --- a/packages/rn-tester/RNTester/NativeExampleViews/FlexibleSizeExampleView.mm +++ b/packages/rn-tester/RNTester/NativeExampleViews/FlexibleSizeExampleView.mm @@ -46,9 +46,8 @@ - (instancetype)initWithFrame:(CGRect)frame AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; - _resizableRootView = [[RCTRootView alloc] initWithBridge:appDelegate.bridge - moduleName:@"RootViewSizeFlexibilityExampleApp" - initialProperties:@{}]; + _resizableRootView = + (RCTRootView *)[appDelegate.rootViewFactory viewWithModuleName:@"RootViewSizeFlexibilityExampleApp"]; [_resizableRootView setSizeFlexibility:RCTRootViewSizeFlexibilityHeight]; diff --git a/packages/rn-tester/RNTester/NativeExampleViews/UpdatePropertiesExampleView.mm b/packages/rn-tester/RNTester/NativeExampleViews/UpdatePropertiesExampleView.mm index 9689203576c2cd..5682d7ddead216 100644 --- a/packages/rn-tester/RNTester/NativeExampleViews/UpdatePropertiesExampleView.mm +++ b/packages/rn-tester/RNTester/NativeExampleViews/UpdatePropertiesExampleView.mm @@ -41,9 +41,8 @@ - (instancetype)initWithFrame:(CGRect)frame AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; - _rootView = [[RCTRootView alloc] initWithBridge:appDelegate.bridge - moduleName:@"SetPropertiesExampleApp" - initialProperties:@{@"color" : @"beige"}]; + _rootView = (RCTRootView *)[appDelegate.rootViewFactory viewWithModuleName:@"SetPropertiesExampleApp" + initialProperties:@{@"color" : @"beige"}]; _button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [_button setTitle:@"Native Button" forState:UIControlStateNormal]; diff --git a/packages/rn-tester/RNTester/PrivacyInfo.xcprivacy b/packages/rn-tester/RNTester/PrivacyInfo.xcprivacy new file mode 100644 index 00000000000000..41b8317f0652b9 --- /dev/null +++ b/packages/rn-tester/RNTester/PrivacyInfo.xcprivacy @@ -0,0 +1,37 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj index edfdf6aa584cb7..57d2da2a0bf129 100644 --- a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj +++ b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj @@ -7,15 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 00915AE8006CF5DB156153DD /* Pods_RNTester.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9DA138FE541ED31A6C589D7 /* Pods_RNTester.framework */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 233478CE9E17A00ACD643BA1 /* libPods-RNTesterIntegrationTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D799EF6AA275D5D4239C033E /* libPods-RNTesterIntegrationTests.a */; }; 2DDEF0101F84BF7B00DBDF73 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DDEF00F1F84BF7B00DBDF73 /* Images.xcassets */; }; 383889DA23A7398900D06C3E /* RCTConvert_UIColorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 383889D923A7398900D06C3E /* RCTConvert_UIColorTests.m */; }; 3D2AFAF51D646CF80089D1A3 /* legacy_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */; }; 5C60EB1C226440DB0018C04F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C60EB1B226440DB0018C04F /* AppDelegate.mm */; }; + 75CCE8D3149414440F8F375F /* libPods-RNTester.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDC71B375B04D2F3DD483B6C /* libPods-RNTester.a */; }; 8145AE06241172D900A3F8DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */; }; 832F45BB2A8A6E1F0097B4E6 /* SwiftTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832F45BA2A8A6E1F0097B4E6 /* SwiftTest.swift */; }; - 836E54623F6567BB812F3F6A /* Pods_RNTesterUnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B5084412F9118F6F7FA99DA /* Pods_RNTesterUnitTests.framework */; }; + A984E3435258D1FA0F157FCE /* libPods-RNTesterUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C82BC2D67C2D9CE6FD31F2FB /* libPods-RNTesterUnitTests.a */; }; CD10C7A5290BD4EB0033E1ED /* RCTEventEmitterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CD10C7A4290BD4EB0033E1ED /* RCTEventEmitterTests.m */; }; E62F11832A5C6580000BF1C8 /* FlexibleSizeExampleView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.mm */; }; E62F11842A5C6584000BF1C8 /* UpdatePropertiesExampleView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.mm */; }; @@ -55,7 +56,7 @@ E7DB216422B2F3EC005AC45F /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7DB215F22B2F3EC005AC45F /* RCTUIManagerScenarioTests.m */; }; E7DB216722B2F69F005AC45F /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7DB213022B2C649005AC45F /* JavaScriptCore.framework */; }; E7DB218C22B41FCD005AC45F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7DB218B22B41FCD005AC45F /* XCTest.framework */; }; - FA45ED19FFAECF4CFEFE0DC7 /* Pods_RNTesterIntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87E71F82CA94BF2D3CA3110A /* Pods_RNTesterIntegrationTests.framework */; }; + F0D621C32BBB9E38005960AC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F0D621C22BBB9E38005960AC /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -76,30 +77,29 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 03BBB27152ED31C66CBDCE00 /* Pods-RNTesterUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests.debug.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* RNTester.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RNTester.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = RNTester/AppDelegate.h; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RNTester/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = RNTester/main.m; sourceTree = ""; }; 272E6B3B1BEA849E001FCF37 /* UpdatePropertiesExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UpdatePropertiesExampleView.h; path = RNTester/NativeExampleViews/UpdatePropertiesExampleView.h; sourceTree = ""; }; 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = UpdatePropertiesExampleView.mm; path = RNTester/NativeExampleViews/UpdatePropertiesExampleView.mm; sourceTree = ""; }; - 2734C5E31C1D7A09BF872585 /* Pods-RNTester.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester.debug.xcconfig"; path = "Target Support Files/Pods-RNTester/Pods-RNTester.debug.xcconfig"; sourceTree = ""; }; 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FlexibleSizeExampleView.mm; path = RNTester/NativeExampleViews/FlexibleSizeExampleView.mm; sourceTree = ""; }; 27F441EA1BEBE5030039B79C /* FlexibleSizeExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexibleSizeExampleView.h; path = RNTester/NativeExampleViews/FlexibleSizeExampleView.h; sourceTree = ""; }; 2DDEF00F1F84BF7B00DBDF73 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RNTester/Images.xcassets; sourceTree = ""; }; - 359825B9A5AE4A3F4AA612DD /* Pods-RNTesterUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests.debug.xcconfig"; sourceTree = ""; }; 383889D923A7398900D06C3E /* RCTConvert_UIColorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_UIColorTests.m; sourceTree = ""; }; - 3B5084412F9118F6F7FA99DA /* Pods_RNTesterUnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RNTesterUnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "legacy_image@2x.png"; path = "RNTester/legacy_image@2x.png"; sourceTree = ""; }; 5C60EB1B226440DB0018C04F /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = RNTester/AppDelegate.mm; sourceTree = ""; }; - 66C3087F2D5BF762FE9E6422 /* Pods-RNTesterIntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterIntegrationTests.debug.xcconfig"; path = "Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests.debug.xcconfig"; sourceTree = ""; }; - 7CDA7A212644C6BB8C0D00D8 /* Pods-RNTesterIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests.release.xcconfig"; sourceTree = ""; }; 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = RNTester/LaunchScreen.storyboard; sourceTree = ""; }; 832F45BA2A8A6E1F0097B4E6 /* SwiftTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SwiftTest.swift; path = RNTester/SwiftTest.swift; sourceTree = ""; }; - 87E71F82CA94BF2D3CA3110A /* Pods_RNTesterIntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RNTesterIntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8BFB9C61D7BDE894E24BF24F /* Pods-RNTesterUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterUnitTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests.release.xcconfig"; sourceTree = ""; }; - 9B8542B8C590B51BD0588751 /* Pods-RNTester.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester.release.xcconfig"; path = "Target Support Files/Pods-RNTester/Pods-RNTester.release.xcconfig"; sourceTree = ""; }; + 904056D7E94DD763B337F4C4 /* Pods-RNTester.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester.debug.xcconfig"; path = "Target Support Files/Pods-RNTester/Pods-RNTester.debug.xcconfig"; sourceTree = ""; }; + 9B69C63956A5B8450AF0E810 /* Pods-RNTester.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester.release.xcconfig"; path = "Target Support Files/Pods-RNTester/Pods-RNTester.release.xcconfig"; sourceTree = ""; }; AC474BFB29BBD4A1002BDAED /* RNTester.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = RNTester.xctestplan; path = RNTester/RNTester.xctestplan; sourceTree = ""; }; + AE6E0BFC1CD49F0DC093531C /* Pods-RNTesterIntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterIntegrationTests.debug.xcconfig"; path = "Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests.debug.xcconfig"; sourceTree = ""; }; + AED50FC94A0BE7A3D33231E0 /* Pods-RNTesterUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterUnitTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests.release.xcconfig"; sourceTree = ""; }; + C82BC2D67C2D9CE6FD31F2FB /* libPods-RNTesterUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTesterUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; CD10C7A4290BD4EB0033E1ED /* RCTEventEmitterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventEmitterTests.m; sourceTree = ""; }; + D799EF6AA275D5D4239C033E /* libPods-RNTesterIntegrationTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTesterIntegrationTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E771AEEA22B44E3100EA1189 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RNTester/Info.plist; sourceTree = ""; }; E7C1241922BEC44B00DA25C0 /* RNTesterIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNTesterIntegrationTests.m; sourceTree = ""; }; E7DB209F22B2BA84005AC45F /* RNTesterUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNTesterUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -161,7 +161,9 @@ E7DB215E22B2F3EC005AC45F /* RCTLoggingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLoggingTests.m; sourceTree = ""; }; E7DB215F22B2F3EC005AC45F /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; E7DB218B22B41FCD005AC45F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = XCTest.framework; sourceTree = DEVELOPER_DIR; }; - F9DA138FE541ED31A6C589D7 /* Pods_RNTester.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RNTester.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EB39930F368F8A8EF2223131 /* Pods-RNTesterIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests.release.xcconfig"; sourceTree = ""; }; + F0D621C22BBB9E38005960AC /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + FDC71B375B04D2F3DD483B6C /* libPods-RNTester.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTester.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -169,7 +171,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 00915AE8006CF5DB156153DD /* Pods_RNTester.framework in Frameworks */, + 75CCE8D3149414440F8F375F /* libPods-RNTester.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -178,7 +180,7 @@ buildActionMask = 2147483647; files = ( E7DB213122B2C649005AC45F /* JavaScriptCore.framework in Frameworks */, - 836E54623F6567BB812F3F6A /* Pods_RNTesterUnitTests.framework in Frameworks */, + A984E3435258D1FA0F157FCE /* libPods-RNTesterUnitTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -188,7 +190,7 @@ files = ( E7DB218C22B41FCD005AC45F /* XCTest.framework in Frameworks */, E7DB216722B2F69F005AC45F /* JavaScriptCore.framework in Frameworks */, - FA45ED19FFAECF4CFEFE0DC7 /* Pods_RNTesterIntegrationTests.framework in Frameworks */, + 233478CE9E17A00ACD643BA1 /* libPods-RNTesterIntegrationTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -207,6 +209,7 @@ 13B07FAE1A68108700A75B9A /* RNTester */ = { isa = PBXGroup; children = ( + F0D621C22BBB9E38005960AC /* PrivacyInfo.xcprivacy */, AC474BFB29BBD4A1002BDAED /* RNTester.xctestplan */, E771AEEA22B44E3100EA1189 /* Info.plist */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, @@ -258,9 +261,9 @@ E7DB211822B2BD53005AC45F /* libReact-RCTText.a */, E7DB211A22B2BD53005AC45F /* libReact-RCTVibration.a */, E7DB212222B2BD53005AC45F /* libyoga.a */, - F9DA138FE541ED31A6C589D7 /* Pods_RNTester.framework */, - 87E71F82CA94BF2D3CA3110A /* Pods_RNTesterIntegrationTests.framework */, - 3B5084412F9118F6F7FA99DA /* Pods_RNTesterUnitTests.framework */, + FDC71B375B04D2F3DD483B6C /* libPods-RNTester.a */, + D799EF6AA275D5D4239C033E /* libPods-RNTesterIntegrationTests.a */, + C82BC2D67C2D9CE6FD31F2FB /* libPods-RNTesterUnitTests.a */, ); name = Frameworks; sourceTree = ""; @@ -300,12 +303,12 @@ E23BD6487B06BD71F1A86914 /* Pods */ = { isa = PBXGroup; children = ( - 2734C5E31C1D7A09BF872585 /* Pods-RNTester.debug.xcconfig */, - 9B8542B8C590B51BD0588751 /* Pods-RNTester.release.xcconfig */, - 66C3087F2D5BF762FE9E6422 /* Pods-RNTesterIntegrationTests.debug.xcconfig */, - 7CDA7A212644C6BB8C0D00D8 /* Pods-RNTesterIntegrationTests.release.xcconfig */, - 359825B9A5AE4A3F4AA612DD /* Pods-RNTesterUnitTests.debug.xcconfig */, - 8BFB9C61D7BDE894E24BF24F /* Pods-RNTesterUnitTests.release.xcconfig */, + 904056D7E94DD763B337F4C4 /* Pods-RNTester.debug.xcconfig */, + 9B69C63956A5B8450AF0E810 /* Pods-RNTester.release.xcconfig */, + AE6E0BFC1CD49F0DC093531C /* Pods-RNTesterIntegrationTests.debug.xcconfig */, + EB39930F368F8A8EF2223131 /* Pods-RNTesterIntegrationTests.release.xcconfig */, + 03BBB27152ED31C66CBDCE00 /* Pods-RNTesterUnitTests.debug.xcconfig */, + AED50FC94A0BE7A3D33231E0 /* Pods-RNTesterUnitTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -369,14 +372,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RNTester" */; buildPhases = ( - ABDE2A52ACD1B95E14790B5E /* [CP] Check Pods Manifest.lock */, + 1D61E03B2C15283212491D88 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 68CD48B71D2BCB2C007E06A9 /* Build JS Bundle */, 79E8BE2B119D4C5CCD2F04B3 /* [RN] Copy Hermes Framework */, - 02B6FEF7E86B613B42F31284 /* [CP] Embed Pods Frameworks */, - 5625E703156DD564DE9175B0 /* [CP] Copy Pods Resources */, + A41785EFA563389E1508FB58 /* [CP] Embed Pods Frameworks */, + 5E028B80F3C32FE3D52DF9A7 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -391,12 +394,12 @@ isa = PBXNativeTarget; buildConfigurationList = E7DB20A622B2BA84005AC45F /* Build configuration list for PBXNativeTarget "RNTesterUnitTests" */; buildPhases = ( - 4F76596957F7356516B534CE /* [CP] Check Pods Manifest.lock */, + 9E05A96E88B51366B482536F /* [CP] Check Pods Manifest.lock */, E7DB209B22B2BA84005AC45F /* Sources */, E7DB209C22B2BA84005AC45F /* Frameworks */, E7DB209D22B2BA84005AC45F /* Resources */, - A904658C20543C2EDC217D15 /* [CP] Embed Pods Frameworks */, - 01934C30687B8C926E4F59CD /* [CP] Copy Pods Resources */, + 3FA5A4AA0B18E37634BDB153 /* [CP] Embed Pods Frameworks */, + 829D7C63C528493FD8CA45E9 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -412,12 +415,12 @@ isa = PBXNativeTarget; buildConfigurationList = E7DB215A22B2F332005AC45F /* Build configuration list for PBXNativeTarget "RNTesterIntegrationTests" */; buildPhases = ( - B7EB74515CDE78D98087DD53 /* [CP] Check Pods Manifest.lock */, + 8AA5E9AFEED3571ADC48A9E6 /* [CP] Check Pods Manifest.lock */, E7DB214F22B2F332005AC45F /* Sources */, E7DB215022B2F332005AC45F /* Frameworks */, E7DB215122B2F332005AC45F /* Resources */, - 4F27ACC9DB890B37D6C267F1 /* [CP] Embed Pods Frameworks */, - E446637427ECD101CAACE52B /* [CP] Copy Pods Resources */, + 659CD3193D6E6ED5B92470CE /* [CP] Embed Pods Frameworks */, + C5A1A0DE195B37E2DE72DBE8 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -477,6 +480,7 @@ files = ( 2DDEF0101F84BF7B00DBDF73 /* Images.xcassets in Resources */, 8145AE06241172D900A3F8DA /* LaunchScreen.storyboard in Resources */, + F0D621C32BBB9E38005960AC /* PrivacyInfo.xcprivacy in Resources */, 3D2AFAF51D646CF80089D1A3 /* legacy_image@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -499,94 +503,77 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 01934C30687B8C926E4F59CD /* [CP] Copy Pods Resources */ = { + 1D61E03B2C15283212491D88 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 02B6FEF7E86B613B42F31284 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RNTester-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 4F27ACC9DB890B37D6C267F1 /* [CP] Embed Pods Frameworks */ = { + 3FA5A4AA0B18E37634BDB153 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 4F76596957F7356516B534CE /* [CP] Check Pods Manifest.lock */ = { + 5E028B80F3C32FE3D52DF9A7 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNTesterUnitTests-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 5625E703156DD564DE9175B0 /* [CP] Copy Pods Resources */ = { + 659CD3193D6E6ED5B92470CE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 68CD48B71D2BCB2C007E06A9 /* Build JS Bundle */ = { @@ -623,24 +610,24 @@ shellPath = /bin/sh; shellScript = ". ../react-native/sdks/hermes-engine/utils/copy-hermes-xcode.sh\n"; }; - A904658C20543C2EDC217D15 /* [CP] Embed Pods Frameworks */ = { + 829D7C63C528493FD8CA45E9 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - ABDE2A52ACD1B95E14790B5E /* [CP] Check Pods Manifest.lock */ = { + 8AA5E9AFEED3571ADC48A9E6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -655,14 +642,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNTester-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RNTesterIntegrationTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B7EB74515CDE78D98087DD53 /* [CP] Check Pods Manifest.lock */ = { + 9E05A96E88B51366B482536F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -677,14 +664,31 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNTesterIntegrationTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RNTesterUnitTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - E446637427ECD101CAACE52B /* [CP] Copy Pods Resources */ = { + A41785EFA563389E1508FB58 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C5A1A0DE195B37E2DE72DBE8 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -782,7 +786,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2734C5E31C1D7A09BF872585 /* Pods-RNTester.debug.xcconfig */; + baseConfigurationReference = 904056D7E94DD763B337F4C4 /* Pods-RNTester.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -820,7 +824,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9B8542B8C590B51BD0588751 /* Pods-RNTester.release.xcconfig */; + baseConfigurationReference = 9B69C63956A5B8450AF0E810 /* Pods-RNTester.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -859,6 +863,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; @@ -889,6 +894,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + CXX = ""; ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -925,10 +931,12 @@ "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD = ""; + LDPLUSPLUS = ""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; @@ -959,6 +967,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; @@ -989,6 +998,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; + CXX = ""; ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1018,10 +1028,12 @@ "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD = ""; + LDPLUSPLUS = ""; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = ( @@ -1050,7 +1062,7 @@ }; E7DB20A722B2BA84005AC45F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 359825B9A5AE4A3F4AA612DD /* Pods-RNTesterUnitTests.debug.xcconfig */; + baseConfigurationReference = 03BBB27152ED31C66CBDCE00 /* Pods-RNTesterUnitTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1088,7 +1100,7 @@ }; E7DB20A822B2BA84005AC45F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8BFB9C61D7BDE894E24BF24F /* Pods-RNTesterUnitTests.release.xcconfig */; + baseConfigurationReference = AED50FC94A0BE7A3D33231E0 /* Pods-RNTesterUnitTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1126,7 +1138,7 @@ }; E7DB215B22B2F332005AC45F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 66C3087F2D5BF762FE9E6422 /* Pods-RNTesterIntegrationTests.debug.xcconfig */; + baseConfigurationReference = AE6E0BFC1CD49F0DC093531C /* Pods-RNTesterIntegrationTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -1165,7 +1177,7 @@ }; E7DB215C22B2F332005AC45F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7CDA7A212644C6BB8C0D00D8 /* Pods-RNTesterIntegrationTests.release.xcconfig */; + baseConfigurationReference = EB39930F368F8A8EF2223131 /* Pods-RNTesterIntegrationTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; diff --git a/packages/rn-tester/android/app/build.gradle.kts b/packages/rn-tester/android/app/build.gradle.kts index 5ad05a044325fd..e2c69a64985e34 100644 --- a/packages/rn-tester/android/app/build.gradle.kts +++ b/packages/rn-tester/android/app/build.gradle.kts @@ -151,6 +151,7 @@ android { dependencies { // Build React Native from source implementation(project(":packages:react-native:ReactAndroid")) + implementation(project(":packages:react-native-popup-menu-android:android")) // Consume Hermes as built from source only for the Hermes variant. "hermesImplementation"(project(":packages:react-native:ReactAndroid:hermes-engine")) diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt index 33b2373fd46c27..8d863a242ca2cf 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt @@ -25,6 +25,7 @@ import com.facebook.react.defaults.DefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.popupmenu.PopupMenuPackage import com.facebook.react.shell.MainReactPackage import com.facebook.react.uiapp.component.MyLegacyViewManager import com.facebook.react.uiapp.component.MyNativeViewManager @@ -44,6 +45,7 @@ class RNTesterApplication : Application(), ReactApplication { public override fun getPackages(): List { return listOf( MainReactPackage(), + PopupMenuPackage(), object : TurboReactPackage() { override fun getModule( name: String, @@ -109,11 +111,13 @@ class RNTesterApplication : Application(), ReactApplication { override fun createViewManager( reactContext: ReactApplicationContext, viewManagerName: String - ): ViewManager<*, out ReactShadowNode<*>> = + ): ViewManager<*, out ReactShadowNode<*>>? = if (viewManagerName == "RNTMyNativeView") { MyNativeViewManager() - } else { + } else if (viewManagerName == "RNTMyLegacyNativeView") { MyLegacyViewManager(reactContext) + } else { + null } }) } diff --git a/packages/rn-tester/js/examples/Cursor/CursorExample.js b/packages/rn-tester/js/examples/Cursor/CursorExample.js new file mode 100644 index 00000000000000..77936112305aff --- /dev/null +++ b/packages/rn-tester/js/examples/Cursor/CursorExample.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +'use strict'; + +const React = require('react'); +const {StyleSheet, Text, View} = require('react-native'); + +const styles = StyleSheet.create({ + invisibleBox: { + width: 100, + height: 100, + }, + box: { + width: 100, + height: 100, + borderWidth: 2, + }, + circle: { + width: 100, + height: 100, + borderWidth: 2, + borderRadius: 100, + }, + halfcircle: { + width: 100, + height: 100, + borderWidth: 2, + borderTopStartRadius: 100, + borderBottomStartRadius: 100, + }, + solid: { + backgroundColor: 'blue', + }, + pointer: { + cursor: 'pointer', + }, + row: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + }, + centerContent: { + justifyContent: 'center', + alignItems: 'center', + }, +}); + +function CursorExampleAuto() { + return ( + + + + + + + + + ); +} + +function CursorExamplePointer() { + return ( + + + + + + + + + ); +} + +function CursorExamplePointer() { + return ( + + + + + + + + + ); +} + +function CursorExampleViewFlattening() { + return ( + + + pointer + + + ); +} + +exports.title = 'Cursor'; +exports.category = 'UI'; +exports.description = + 'Demonstrates setting a cursor, which affects the appearance when a pointer is over the View.'; +exports.examples = [ + { + title: 'Default', + description: "Cursor: 'auto' or no cursor set ", + render: CursorExampleAuto, + }, + { + title: 'Pointer', + description: 'cursor: pointer', + render: CursorExamplePointer, + }, + { + title: 'View flattening', + description: 'Views with a cursor do not get flattened', + render: CursorExampleViewFlattening, + }, +]; diff --git a/packages/rn-tester/js/examples/PopupMenuAndroid/PopupMenuAndroidExample.js b/packages/rn-tester/js/examples/PopupMenuAndroid/PopupMenuAndroidExample.js index 2b35119e662ed8..3934c0f4f08307 100644 --- a/packages/rn-tester/js/examples/PopupMenuAndroid/PopupMenuAndroidExample.js +++ b/packages/rn-tester/js/examples/PopupMenuAndroid/PopupMenuAndroidExample.js @@ -10,11 +10,12 @@ 'use strict'; +import type {PopupMenuAndroidInstance} from '@react-native/popup-menu-android'; import type {Node} from 'react'; -import type {PopupMenuAndroidInstance} from 'react-native/Libraries/Components/PopupMenuAndroid/PopupMenuAndroid'; +import PopupMenuAndroid from '@react-native/popup-menu-android'; import * as React from 'react'; -import {Button, PopupMenuAndroid, StyleSheet, Text, View} from 'react-native'; +import {Button, StyleSheet, Text, View} from 'react-native'; type Fruit = 'Apple' | 'Pear' | 'Banana' | 'Orange' | 'Kiwi'; diff --git a/packages/rn-tester/js/utils/RNTesterList.ios.js b/packages/rn-tester/js/utils/RNTesterList.ios.js index fa0587ccd1454c..4dbcabd8b3f728 100644 --- a/packages/rn-tester/js/utils/RNTesterList.ios.js +++ b/packages/rn-tester/js/utils/RNTesterList.ios.js @@ -195,6 +195,10 @@ const APIs: Array = ([ key: 'CrashExample', module: require('../examples/Crash/CrashExample'), }, + { + key: 'CursorExample', + module: require('../examples/Cursor/CursorExample'), + }, { key: 'DevSettings', module: require('../examples/DevSettings/DevSettingsExample'), diff --git a/packages/rn-tester/metro.config.js b/packages/rn-tester/metro.config.js index 3c41387cc19b65..87b72ad0f6c214 100644 --- a/packages/rn-tester/metro.config.js +++ b/packages/rn-tester/metro.config.js @@ -30,6 +30,7 @@ const config = { path.resolve(__dirname, '../polyfills'), path.resolve(__dirname, '../react-native'), path.resolve(__dirname, '../virtualized-lists'), + path.resolve(__dirname, '../react-native-popup-menu-android'), ], resolver: { blockList: [/..\/react-native\/sdks\/hermes/], diff --git a/packages/rn-tester/package.json b/packages/rn-tester/package.json index d63abf26aab14e..dd55c2c0d580c2 100644 --- a/packages/rn-tester/package.json +++ b/packages/rn-tester/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/tester", - "version": "0.0.1", + "version": "0.74.84", "private": true, "description": "React Native tester app.", "license": "MIT", @@ -25,7 +25,8 @@ "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "nullthrows": "^1.1.1" + "nullthrows": "^1.1.1", + "@react-native/popup-menu-android": "0.74.84" }, "peerDependencies": { "react": "18.2.0", diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 0ca29efbb837a5..92e633652ae579 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/typescript-config", - "version": "0.74.0", + "version": "0.74.84", "description": "Default TypeScript configuration for React Native apps", "license": "MIT", "repository": { diff --git a/packages/virtualized-lists/package.json b/packages/virtualized-lists/package.json index b05d544bd85d55..791a5ba68c2a23 100644 --- a/packages/virtualized-lists/package.json +++ b/packages/virtualized-lists/package.json @@ -1,6 +1,6 @@ { "name": "@react-native/virtualized-lists", - "version": "0.74.0", + "version": "0.74.84", "description": "Virtualized lists for React Native.", "license": "MIT", "repository": { @@ -27,7 +27,13 @@ "react-test-renderer": "18.2.0" }, "peerDependencies": { + "@types/react": "^18.2.6", "react": "*", "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } } diff --git a/scripts/__tests__/npm-utils-test.js b/scripts/__tests__/npm-utils-test.js index 0413bb54f5b6cc..7b4345256d5b05 100644 --- a/scripts/__tests__/npm-utils-test.js +++ b/scripts/__tests__/npm-utils-test.js @@ -10,13 +10,13 @@ const { applyPackageVersions, getNpmInfo, - getPackageVersionStrByTag, getVersionsBySpec, publishPackage, } = require('../npm-utils'); const execMock = jest.fn(); const getCurrentCommitMock = jest.fn(); +const exitIfNotOnGitMock = jest.fn(); jest.mock('shelljs', () => ({ exec: execMock, @@ -24,6 +24,7 @@ jest.mock('shelljs', () => ({ jest.mock('./../scm-utils', () => ({ getCurrentCommit: getCurrentCommitMock, + exitIfNotOnGit: exitIfNotOnGitMock, })); describe('npm-utils', () => { @@ -73,26 +74,6 @@ describe('npm-utils', () => { }); }); - describe('getPackageVersionStrByTag', () => { - it('should return package version string', () => { - execMock.mockImplementationOnce(() => ({code: 0, stdout: '0.34.2 \n'})); - const versionStr = getPackageVersionStrByTag('my-package', 'next'); - expect(versionStr).toBe('0.34.2'); - }); - it('should throw error when invalid result', () => { - execMock.mockImplementationOnce(() => ({ - code: 1, - stderr: 'Some error message', - })); - - expect(() => { - getPackageVersionStrByTag('my-package', 'next'); - }).toThrow( - "Failed to run 'npm view my-package@next version'\nSome error message", - ); - }); - }); - describe('publishPackage', () => { it('should run publish command', () => { publishPackage( @@ -124,6 +105,13 @@ describe('npm-utils', () => { {cwd: 'path/to/my-package'}, ); }); + + it('should handle -no-tag', () => { + publishPackage('path/to/my-package', {tags: ['--no-tag'], otp: 'otp'}); + expect(execMock).toHaveBeenCalledWith('npm publish --no-tag --otp otp', { + cwd: 'path/to/my-package', + }); + }); }); describe('getNpmInfo', () => { @@ -138,6 +126,29 @@ describe('npm-utils', () => { tag: 'prealpha', }); }); + + it('return the expected format for patch-prereleases', () => { + const isoStringSpy = jest.spyOn(Date.prototype, 'toISOString'); + isoStringSpy.mockReturnValue('2023-10-04T15:43:55.123Z'); + getCurrentCommitMock.mockImplementation(() => 'abcd1234'); + // exitIfNotOnGit takes a function as a param and it: + // 1. checks if we are on git => if not it exits + // 2. run the function passed as a param and return the output to the caller + // For the mock, we are assuming we are on github and we are returning `false` + // as the `getNpmInfo` function will pass a function that checks if the + // current commit is a tagged with 'latest'. + // In the Mock, we are assuming that we are on git (it does not exits) and the + // checkIfLatest function returns `false` + exitIfNotOnGitMock.mockImplementation(() => false); + + process.env.CIRCLE_TAG = 'v0.74.1-rc.0'; + const returnedValue = getNpmInfo('release'); + expect(returnedValue).toMatchObject({ + version: `0.74.1-rc.0`, + tag: '--no-tag', + }); + process.env.CIRCLE_TAG = null; + }); }); describe('getVersionsBySpec', () => { diff --git a/scripts/consts.js b/scripts/consts.js index a4528fea7dceb6..6c127fc56c967a 100644 --- a/scripts/consts.js +++ b/scripts/consts.js @@ -36,9 +36,19 @@ const REACT_NATIVE_PACKAGE_DIR /*: string */ = path.join( 'react-native', ); +/** + * The absolute path to the rn-tester package. + */ +const RN_TESTER_DIR /*: string */ = path.join( + REPO_ROOT, + 'packages', + 'rn-tester', +); + module.exports = { PACKAGES_DIR, REACT_NATIVE_PACKAGE_DIR, REPO_ROOT, + RN_TESTER_DIR, SCRIPTS_DIR, }; diff --git a/scripts/e2e/init-template-e2e.js b/scripts/e2e/init-template-e2e.js index 49542fcb730dad..b70e6768c54a08 100644 --- a/scripts/e2e/init-template-e2e.js +++ b/scripts/e2e/init-template-e2e.js @@ -13,7 +13,7 @@ const {retry} = require('../circleci/retry'); const {REPO_ROOT} = require('../consts'); -const forEachPackage = require('../monorepo/for-each-package'); +const {getPackages} = require('../utils/monorepo'); const { VERDACCIO_SERVER_URL, VERDACCIO_STORAGE_PATH, @@ -22,6 +22,7 @@ const { const {parseArgs} = require('@pkgjs/parseargs'); const chalk = require('chalk'); const {execSync} = require('child_process'); +const path = require('path'); const config = { options: { @@ -64,6 +65,10 @@ async function main() { } await initNewProjectFromSource(options); + + // TODO(T179377112): Fix memory leak from `spawn` in `setupVerdaccio` (above + // kill command does not wait for kill success). + process.exit(0); } async function initNewProjectFromSource( @@ -86,26 +91,28 @@ async function initNewProjectFromSource( console.log('\nDone ✅'); console.log('Publishing packages to local npm proxy\n'); - forEachPackage( - (packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => { - if (packageManifest.private) { - return; - } - - const desc = `${packageManifest.name} (${packageRelativePathFromRoot})`; - process.stdout.write( - `${desc} ${chalk.dim('.').repeat(Math.max(0, 72 - desc.length))} `, - ); - execSync( - `npm publish --registry ${VERDACCIO_SERVER_URL} --access public`, - { - cwd: packageAbsolutePath, - stdio: verbose ? 'inherit' : [process.stderr], - }, - ); - process.stdout.write(chalk.reset.inverse.bold.green(' DONE ') + '\n'); - }, - ); + const packages = await getPackages({ + includeReactNative: false, + includePrivate: false, + }); + + for (const {path: packagePath, packageJson} of Object.values(packages)) { + const desc = `${packageJson.name} (${path.relative( + REPO_ROOT, + packagePath, + )})`; + process.stdout.write( + `${desc} ${chalk.dim('.').repeat(Math.max(0, 72 - desc.length))} `, + ); + execSync( + `npm publish --registry ${VERDACCIO_SERVER_URL} --access public`, + { + cwd: packagePath, + stdio: verbose ? 'inherit' : [process.stderr], + }, + ); + process.stdout.write(chalk.reset.inverse.bold.green(' DONE ') + '\n'); + } console.log('\nDone ✅'); console.log('Running react-native init without install'); @@ -114,53 +121,49 @@ async function initNewProjectFromSource( --directory ${directory} \ --template ${templatePath} \ --verbose \ - --skip-install \ - --yarn-config-options npmRegistryServer="${VERDACCIO_SERVER_URL}"`, + --pm npm \ + --skip-install`, { // Avoid loading packages/react-native/react-native.config.js cwd: REPO_ROOT, - stdio: verbose ? 'inherit' : [process.stderr], + stdio: 'inherit', }, ); console.log('\nDone ✅'); console.log('Installing project dependencies'); - await runYarnUsingProxy(directory); + await installProjectUsingProxy(directory); console.log('Done ✅'); } catch (e) { console.log('Failed ❌'); throw e; } finally { console.log(`Cleanup: Killing Verdaccio process (PID: ${verdaccioPid})`); - execSync(`kill -9 ${verdaccioPid}`); - console.log('Done ✅'); + try { + execSync(`kill -9 ${verdaccioPid}`); + console.log('Done ✅'); + } catch { + console.warn('Failed to kill Verdaccio process'); + } console.log('Cleanup: Removing Verdaccio storage directory'); execSync(`rm -rf ${VERDACCIO_STORAGE_PATH}`); console.log('Done ✅'); - - // TODO(huntie): Fix memory leak from `spawn` in `setupVerdaccio` (above - // kill command does not wait for kill success). - process.exit(0); } } -async function runYarnUsingProxy(cwd /*: string */) { +async function installProjectUsingProxy(cwd /*: string */) { const execOptions = { cwd, stdio: 'inherit', }; - execSync( - `yarn config set npmRegistryServer "${VERDACCIO_SERVER_URL}"`, - execOptions, - ); - execSync( - 'yarn config set unsafeHttpWhitelist --json \'["localhost"]\'', - execOptions, - ); // TODO(huntie): Review pre-existing retry limit - const success = await retry('yarn', execOptions, 3, 500, ['install']); + const success = await retry('npm', execOptions, 3, 500, [ + 'install', + '--registry', + VERDACCIO_SERVER_URL, + ]); if (!success) { throw new Error('Failed to install project dependencies'); diff --git a/scripts/e2e/run-ci-e2e-tests.js b/scripts/e2e/run-ci-e2e-tests.js index d76d7a1b6673be..ad3f6d16612668 100644 --- a/scripts/e2e/run-ci-e2e-tests.js +++ b/scripts/e2e/run-ci-e2e-tests.js @@ -22,7 +22,7 @@ */ const {REACT_NATIVE_PACKAGE_DIR, REPO_ROOT, SCRIPTS_DIR} = require('../consts'); -const forEachPackage = require('../monorepo/for-each-package'); +const {getPackages} = require('../utils/monorepo'); const tryExecNTimes = require('./utils/try-n-times'); const {setupVerdaccio} = require('./utils/verdaccio'); const {execFileSync, spawn} = require('child_process'); @@ -47,275 +47,282 @@ function describe(message /*: string */) { echo(`\n\n>>>>> ${message}\n\n\n`); } -try { - if (argv.android) { - describe('Compile Android binaries'); +async function main() { + try { + if (argv.android) { + describe('Compile Android binaries'); + if ( + exec( + './gradlew publishAllToMavenTempLocal -Pjobs=1 -Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"', + ).code + ) { + throw new Error('Failed to compile Android binaries'); + } + } + + describe('Create react-native package'); if ( exec( - './gradlew publishAllToMavenTempLocal -Pjobs=1 -Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"', + 'node ./scripts/releases/set-rn-version.js --to-version 1000.0.0 --build-type dry-run', ).code ) { - throw new Error('Failed to compile Android binaries'); + throw new Error( + 'Failed to set version and update package.json ready for release', + ); } - } - describe('Create react-native package'); - if ( - exec( - 'node ./scripts/releases/set-rn-version.js --to-version 1000.0.0 --build-type dry-run', - ).code - ) { - throw new Error( - 'Failed to set version and update package.json ready for release', - ); - } - - if (exec('npm pack', {cwd: REACT_NATIVE_PACKAGE_DIR}).code) { - throw new Error('Failed to pack react-native'); - } + if (exec('npm pack', {cwd: REACT_NATIVE_PACKAGE_DIR}).code) { + throw new Error('Failed to pack react-native'); + } - const REACT_NATIVE_PACKAGE = path.join( - REACT_NATIVE_PACKAGE_DIR, - 'react-native-*.tgz', - ); + const REACT_NATIVE_PACKAGE = path.join( + REACT_NATIVE_PACKAGE_DIR, + 'react-native-*.tgz', + ); - describe('Set up Verdaccio'); - VERDACCIO_PID = setupVerdaccio(); + describe('Set up Verdaccio'); + VERDACCIO_PID = setupVerdaccio(); - describe('Build and publish packages'); - exec('node ./scripts/build/build.js', {cwd: REPO_ROOT}); + describe('Build and publish packages'); + exec('node ./scripts/build/build.js', {cwd: REPO_ROOT}); - forEachPackage( - (packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => { - if (packageManifest.private) { - return; - } + const packages = await getPackages({ + includeReactNative: false, + includePrivate: false, + }); + for (const {path: packageAbsolutePath} of Object.values(packages)) { exec( 'npm publish --registry http://localhost:4873 --yes --access public', {cwd: packageAbsolutePath}, ); - }, - ); - - describe('Scaffold a basic React Native app from template'); - execFileSync('rsync', [ - '-a', - `${REPO_ROOT}/packages/react-native/template`, - REACT_NATIVE_TEMP_DIR, - ]); - cd(REACT_NATIVE_APP_DIR); - - mv('_bundle', '.bundle'); - mv('_eslintrc.js', '.eslintrc.js'); - mv('_prettierrc.js', '.prettierrc.js'); - mv('_watchmanconfig', '.watchmanconfig'); - fs.writeFileSync('.npmrc', 'registry=http://localhost:4873'); - - describe('Install React Native package'); - exec(`npm install ${REACT_NATIVE_PACKAGE}`); - - describe('Install node_modules'); - if ( - tryExecNTimes( - () => { - return exec('npm install').code; - }, - numberOfRetries, - () => exec('sleep 10s'), - ) - ) { - throw new Error( - 'Failed to execute npm install. Most common reason is npm registry connectivity, try again', - ); - } - exec('rm -rf ./node_modules/react-native/template'); - - if (argv.android) { - describe('Install end-to-end framework'); - if ( - tryExecNTimes( - () => - exec( - 'yarn add --dev appium@1.11.1 mocha@2.4.5 wd@1.11.1 colors@1.0.3 pretty-data2@0.40.1', - {silent: true}, - ).code, - numberOfRetries, - ) - ) { - throw new Error( - 'Failed to install appium. Most common reason is npm registry connectivity, try again.', - ); - } - cp(`${SCRIPTS_DIR}/android-e2e-test.js`, 'android-e2e-test.js'); - cd('android'); - describe('Download Maven deps'); - exec('./gradlew :app:copyDownloadableDepsToLibs'); - cd('..'); - - describe('Generate key'); - exec('rm android/app/debug.keystore'); - if ( - exec( - 'keytool -genkey -v -keystore android/app/debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"', - ).code - ) { - throw new Error('Key could not be generated'); } - // $FlowFixMe[incompatible-type] - describe(`Start appium server, ${APPIUM_PID}`); - const appiumProcess = spawn('node', ['./node_modules/.bin/appium']); - APPIUM_PID = appiumProcess.pid; + describe('Scaffold a basic React Native app from template'); + execFileSync('rsync', [ + '-a', + `${REPO_ROOT}/packages/react-native/template`, + REACT_NATIVE_TEMP_DIR, + ]); + cd(REACT_NATIVE_APP_DIR); - describe('Build the app'); - if (exec('react-native run-android').code) { - throw new Error('Could not execute react-native run-android'); - } + mv('_bundle', '.bundle'); + mv('_eslintrc.js', '.eslintrc.js'); + mv('_prettierrc.js', '.prettierrc.js'); + mv('_watchmanconfig', '.watchmanconfig'); + fs.writeFileSync('.npmrc', 'registry=http://localhost:4873'); - // $FlowFixMe[incompatible-type] - describe(`Start Metro, ${SERVER_PID}`); - // shelljs exec('', {async: true}) does not emit stdout events, so we rely on good old spawn - const packagerProcess = spawn('yarn', ['start', '--max-workers 1']); - SERVER_PID = packagerProcess.pid; - // wait a bit to allow packager to startup - exec('sleep 15s'); - describe('Test: Android end-to-end test'); + describe('Install React Native package'); + exec(`npm install ${REACT_NATIVE_PACKAGE}`); + + describe('Install node_modules'); if ( tryExecNTimes( () => { - return exec('node node_modules/.bin/_mocha android-e2e-test.js').code; + return exec('npm install').code; }, numberOfRetries, () => exec('sleep 10s'), ) ) { throw new Error( - 'Failed to run Android end-to-end tests. Most likely the code is broken.', + 'Failed to execute npm install. Most common reason is npm registry connectivity, try again', ); } - } + exec('rm -rf ./node_modules/react-native/template'); - if (argv.ios) { - cd('ios'); - // shelljs exec('', {async: true}) does not emit stdout events, so we rely on good old spawn - const packagerEnv = Object.create(process.env); - // $FlowFixMe[prop-missing] - packagerEnv.REACT_NATIVE_MAX_WORKERS = 1; - describe('Start Metro'); - const packagerProcess = spawn('yarn', ['start'], { - stdio: 'inherit', - env: packagerEnv, - }); - SERVER_PID = packagerProcess.pid; - exec('sleep 15s'); - // prepare cache to reduce chances of possible red screen "Can't find variable __fbBatchedBridge..." - exec( - 'response=$(curl --write-out %{http_code} --silent --output /dev/null localhost:8081/index.bundle?platform=ios&dev=true)', - ); - echo(`Metro is running, ${SERVER_PID}`); + if (argv.android) { + describe('Install end-to-end framework'); + if ( + tryExecNTimes( + () => + exec( + 'yarn add --dev appium@1.11.1 mocha@2.4.5 wd@1.11.1 colors@1.0.3 pretty-data2@0.40.1', + {silent: true}, + ).code, + numberOfRetries, + ) + ) { + throw new Error( + 'Failed to install appium. Most common reason is npm registry connectivity, try again.', + ); + } + cp(`${SCRIPTS_DIR}/android-e2e-test.js`, 'android-e2e-test.js'); + cd('android'); + describe('Download Maven deps'); + exec('./gradlew :app:copyDownloadableDepsToLibs'); + cd('..'); - describe('Install CocoaPod dependencies'); - exec('bundle exec pod install'); + describe('Generate key'); + exec('rm android/app/debug.keystore'); + if ( + exec( + 'keytool -genkey -v -keystore android/app/debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"', + ).code + ) { + throw new Error('Key could not be generated'); + } - describe('Test: iOS end-to-end test'); - if ( - // TODO: Get target OS and simulator from .tests.env - tryExecNTimes( - () => { - return exec( - [ - 'xcodebuild', - '-workspace', - '"HelloWorld.xcworkspace"', - '-destination', - '"platform=iOS Simulator,name=iPhone 8,OS=13.3"', - '-scheme', - '"HelloWorld"', - '-sdk', - 'iphonesimulator', - '-UseModernBuildSystem=NO', - 'test', - ].join(' ') + - ' | ' + - [ - 'xcbeautify', - '--report', - 'junit', - '--reportPath', - '"~/react-native/reports/junit/iOS-e2e/results.xml"', - ].join(' ') + - ' && exit ${PIPESTATUS[0]}', - ).code; - }, - numberOfRetries, - () => exec('sleep 10s'), - ) - ) { - throw new Error( - 'Failed to run iOS end-to-end tests. Most likely the code is broken.', - ); - } - cd('..'); - } + // $FlowFixMe[incompatible-type] + describe(`Start appium server, ${APPIUM_PID}`); + const appiumProcess = spawn('node', ['./node_modules/.bin/appium']); + APPIUM_PID = appiumProcess.pid; - if (argv.js) { - // Check the packager produces a bundle (doesn't throw an error) - describe('Test: Verify packager can generate an Android bundle'); - if ( - exec( - 'yarn react-native bundle --verbose --entry-file index.js --platform android --dev true --bundle-output android-bundle.js --max-workers 1', - ).code - ) { - throw new Error('Could not build Android bundle'); + describe('Build the app'); + if (exec('react-native run-android').code) { + throw new Error('Could not execute react-native run-android'); + } + + // $FlowFixMe[incompatible-type] + describe(`Start Metro, ${SERVER_PID}`); + // shelljs exec('', {async: true}) does not emit stdout events, so we rely on good old spawn + const packagerProcess = spawn('yarn', ['start', '--max-workers 1']); + SERVER_PID = packagerProcess.pid; + // wait a bit to allow packager to startup + exec('sleep 15s'); + describe('Test: Android end-to-end test'); + if ( + tryExecNTimes( + () => { + return exec('node node_modules/.bin/_mocha android-e2e-test.js') + .code; + }, + numberOfRetries, + () => exec('sleep 10s'), + ) + ) { + throw new Error( + 'Failed to run Android end-to-end tests. Most likely the code is broken.', + ); + } } - describe('Test: Verify packager can generate an iOS bundle'); - if ( + if (argv.ios) { + cd('ios'); + // shelljs exec('', {async: true}) does not emit stdout events, so we rely on good old spawn + const packagerEnv = Object.create(process.env); + // $FlowFixMe[prop-missing] + packagerEnv.REACT_NATIVE_MAX_WORKERS = 1; + describe('Start Metro'); + const packagerProcess = spawn('yarn', ['start'], { + stdio: 'inherit', + env: packagerEnv, + }); + SERVER_PID = packagerProcess.pid; + exec('sleep 15s'); + // prepare cache to reduce chances of possible red screen "Can't find variable __fbBatchedBridge..." exec( - 'yarn react-native bundle --entry-file index.js --platform ios --dev true --bundle-output ios-bundle.js --max-workers 1', - ).code - ) { - throw new Error('Could not build iOS bundle'); - } + 'response=$(curl --write-out %{http_code} --silent --output /dev/null localhost:8081/index.bundle?platform=ios&dev=true)', + ); + echo(`Metro is running, ${SERVER_PID}`); - describe('Test: TypeScript typechecking'); - if (exec('yarn tsc').code) { - throw new Error('Typechecking errors were found'); - } + describe('Install CocoaPod dependencies'); + exec('bundle exec pod install'); - describe('Test: Jest tests'); - if (exec('yarn test').code) { - throw new Error('Jest tests failed'); + describe('Test: iOS end-to-end test'); + if ( + // TODO: Get target OS and simulator from .tests.env + tryExecNTimes( + () => { + return exec( + [ + 'xcodebuild', + '-workspace', + '"HelloWorld.xcworkspace"', + '-destination', + '"platform=iOS Simulator,name=iPhone 8,OS=13.3"', + '-scheme', + '"HelloWorld"', + '-sdk', + 'iphonesimulator', + '-UseModernBuildSystem=NO', + 'test', + ].join(' ') + + ' | ' + + [ + 'xcbeautify', + '--report', + 'junit', + '--reportPath', + '"~/react-native/reports/junit/iOS-e2e/results.xml"', + ].join(' ') + + ' && exit ${PIPESTATUS[0]}', + ).code; + }, + numberOfRetries, + () => exec('sleep 10s'), + ) + ) { + throw new Error( + 'Failed to run iOS end-to-end tests. Most likely the code is broken.', + ); + } + cd('..'); } - // TODO: ESLint infinitely hangs when running in the environment created by - // this script, but not projects generated by `react-native init`. - /* + if (argv.js) { + // Check the packager produces a bundle (doesn't throw an error) + describe('Test: Verify packager can generate an Android bundle'); + if ( + exec( + 'yarn react-native bundle --verbose --entry-file index.js --platform android --dev true --bundle-output android-bundle.js --max-workers 1', + ).code + ) { + throw new Error('Could not build Android bundle'); + } + + describe('Test: Verify packager can generate an iOS bundle'); + if ( + exec( + 'yarn react-native bundle --entry-file index.js --platform ios --dev true --bundle-output ios-bundle.js --max-workers 1', + ).code + ) { + throw new Error('Could not build iOS bundle'); + } + + describe('Test: TypeScript typechecking'); + if (exec('yarn tsc').code) { + throw new Error('Typechecking errors were found'); + } + + describe('Test: Jest tests'); + if (exec('yarn test').code) { + throw new Error('Jest tests failed'); + } + + // TODO: ESLint infinitely hangs when running in the environment created by + // this script, but not projects generated by `react-native init`. + /* describe('Test: ESLint/Prettier linting and formatting'); if (exec('yarn lint').code) { echo('linting errors were found'); exitCode = 1; throw Error(exitCode); }*/ + } + exitCode = 0; + } finally { + describe('Clean up'); + if (SERVER_PID) { + echo(`Killing packager ${SERVER_PID}`); + exec(`kill -9 ${SERVER_PID}`); + // this is quite drastic but packager starts a daemon that we can't kill by killing the parent process + // it will be fixed in April (quote David Aurelio), so until then we will kill the zombie by the port number + exec("lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill"); + } + if (APPIUM_PID) { + echo(`Killing appium ${APPIUM_PID}`); + exec(`kill -9 ${APPIUM_PID}`); + } + if (VERDACCIO_PID) { + echo(`Killing verdaccio ${VERDACCIO_PID}`); + exec(`kill -9 ${VERDACCIO_PID}`); + } } - exitCode = 0; -} finally { - describe('Clean up'); - if (SERVER_PID) { - echo(`Killing packager ${SERVER_PID}`); - exec(`kill -9 ${SERVER_PID}`); - // this is quite drastic but packager starts a daemon that we can't kill by killing the parent process - // it will be fixed in April (quote David Aurelio), so until then we will kill the zombie by the port number - exec("lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill"); - } - if (APPIUM_PID) { - echo(`Killing appium ${APPIUM_PID}`); - exec(`kill -9 ${APPIUM_PID}`); - } - if (VERDACCIO_PID) { - echo(`Killing verdaccio ${VERDACCIO_PID}`); - exec(`kill -9 ${VERDACCIO_PID}`); - } + exit(exitCode); +} + +if (require.main === module) { + // eslint-disable-next-line no-void + void main(); } -exit(exitCode); diff --git a/scripts/monorepo/__tests__/__fixtures__/get-and-update-packages-fixtures.js b/scripts/monorepo/__tests__/__fixtures__/get-and-update-packages-fixtures.js deleted file mode 100644 index 4fb2738c7e69c3..00000000000000 --- a/scripts/monorepo/__tests__/__fixtures__/get-and-update-packages-fixtures.js +++ /dev/null @@ -1,174 +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 - * @oncall react-native - */ - -const mockPackages = [ - { - packageManifest: { - name: '@react-native/packageA', - version: 'local-version', - dependencies: {}, - devDependencies: {}, - }, - packageAbsolutePath: '/some/place/packageA', - packageRelativePathFromRoot: './place/packageA', - }, - { - packageManifest: { - name: '@react-native/not_published', - version: 'local-version', - dependencies: {'@react-native/packageA': 'local-version'}, - }, - packageAbsolutePath: '/some/place/not_published', - packageRelativePathFromRoot: './place/not_published', - }, - { - packageManifest: { - name: '@react-native/packageB', - version: 'local-version', - dependencies: {'@react-native/packageA': 'local-version'}, - }, - packageAbsolutePath: '/some/place/packageB', - packageRelativePathFromRoot: './place/packageB', - }, - { - packageManifest: { - name: '@react-native/packageC', - version: 'local-version', - devDependencies: {'@react-native/packageE': 'local-version'}, - }, - packageAbsolutePath: '/some/place/packageC', - packageRelativePathFromRoot: './place/packageC', - }, - { - packageManifest: { - name: '@react-native/packageD', - version: 'local-version', - dependencies: { - '@react-native/packageA': 'local-version', - '@react-native/packageC': 'local-version', - }, - }, - packageAbsolutePath: '/some/place/packageD', - packageRelativePathFromRoot: './place/packageD', - }, - { - packageManifest: {name: '@react-native/packageE', version: 'local-version'}, - packageAbsolutePath: '/some/place/packageE', - packageRelativePathFromRoot: './place/packageE', - }, - { - packageManifest: { - name: '@react-native/packageF', - version: 'local-version', - dependencies: { - '@react-native/packageB': 'local-version', - }, - devDependencies: { - '@react-native/packageD': 'local-version', - }, - }, - packageAbsolutePath: '/some/place/packageF', - packageRelativePathFromRoot: './place/packageF', - }, - { - packageManifest: { - name: '@react-native/packageP', - version: 'local-version', - private: true, - dependencies: {}, - devDependencies: {}, - }, - packageAbsolutePath: '/some/place/packageP', - packageRelativePathFromRoot: './place/packageP', - }, - { - packageManifest: { - name: 'react-native', - version: 'local-version', - private: true, - dependencies: { - '@react-native/packageA': 'local-version', - '@react-native/packageB': 'local-version', - '@react-native/packageC': 'local-version', - }, - devDependencies: { - '@react-native/packageD': 'local-version', - '@react-native/packageE': 'local-version', - '@react-native/packageF': 'local-version', - }, - }, - packageAbsolutePath: '/some/place/react-native', - packageRelativePathFromRoot: './place/react-native', - }, -]; - -function expectedPackageA(newVersion) { - return { - name: '@react-native/packageA', - version: newVersion, - dependencies: {}, - devDependencies: {}, - }; -} - -function expectedPackageB(newVersion) { - return { - name: '@react-native/packageB', - version: newVersion, - dependencies: {'@react-native/packageA': newVersion}, - }; -} - -function expectedPackageC(newVersion) { - return { - name: '@react-native/packageC', - version: newVersion, - devDependencies: {'@react-native/packageE': newVersion}, - }; -} -function expectedPackageD(newVersion) { - return { - name: '@react-native/packageD', - version: newVersion, - dependencies: { - '@react-native/packageA': newVersion, - '@react-native/packageC': newVersion, - }, - }; -} -function expectedPackageE(newVersion) { - return {name: '@react-native/packageE', version: newVersion}; -} -function expectedPackageF(newVersion) { - return { - name: '@react-native/packageF', - version: newVersion, - dependencies: { - '@react-native/packageB': newVersion, - }, - devDependencies: { - '@react-native/packageD': newVersion, - }, - }; -} - -const expectedPackages = { - '@react-native/packageA': expectedPackageA, - '@react-native/packageB': expectedPackageB, - '@react-native/packageC': expectedPackageC, - '@react-native/packageD': expectedPackageD, - '@react-native/packageE': expectedPackageE, - '@react-native/packageF': expectedPackageF, -}; - -module.exports = { - mockPackages, - expectedPackages, -}; diff --git a/scripts/monorepo/__tests__/bump-package-version-test.js b/scripts/monorepo/__tests__/bump-package-version-test.js deleted file mode 100644 index 1481e5ac4083fa..00000000000000 --- a/scripts/monorepo/__tests__/bump-package-version-test.js +++ /dev/null @@ -1,38 +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 - */ - -const bumpPackageVersion = require('../bump-all-updated-packages/bump-package-version'); -const {writeFileSync} = require('fs'); -const path = require('path'); - -jest.mock('fs', () => ({ - writeFileSync: jest.fn(), - readFileSync: jest.fn(() => '{}'), -})); - -jest.mock('../for-each-package', () => callback => {}); - -describe('bumpPackageVersionTest', () => { - it('updates patch version of the package', () => { - const mockedPackageLocation = '~/packages/assets'; - const mockedPackageManifest = { - name: '@react-native/test', - version: '1.2.3', - }; - - bumpPackageVersion(mockedPackageLocation, mockedPackageManifest); - - expect(writeFileSync).toHaveBeenCalledWith( - path.join(mockedPackageLocation, 'package.json'), - JSON.stringify({...mockedPackageManifest, version: '1.2.4'}, null, 2) + - '\n', - 'utf-8', - ); - }); -}); diff --git a/scripts/monorepo/__tests__/for-each-package-test.js b/scripts/monorepo/__tests__/for-each-package-test.js deleted file mode 100644 index c3154d61940702..00000000000000 --- a/scripts/monorepo/__tests__/for-each-package-test.js +++ /dev/null @@ -1,70 +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 - */ - -const forEachPackage = require('../for-each-package'); -const {readdirSync, readFileSync} = require('fs'); -const path = require('path'); - -jest.mock('fs', () => ({ - readdirSync: jest.fn(), - readFileSync: jest.fn(), -})); - -describe('forEachPackage', () => { - it('executes callback call with parameters', () => { - const callback = jest.fn(); - const mockedPackageManifest = '{"name": "my-new-package"}'; - const mockedParsedPackageManifest = JSON.parse(mockedPackageManifest); - const mockedPackageName = 'my-new-package'; - - readdirSync.mockImplementationOnce(() => [ - {name: mockedPackageName, isDirectory: () => true}, - ]); - readFileSync.mockImplementationOnce(() => mockedPackageManifest); - - forEachPackage(callback); - - expect(callback).toHaveBeenCalledWith( - path.join(__dirname, '..', '..', '..', 'packages', mockedPackageName), - path.join('packages', mockedPackageName), - mockedParsedPackageManifest, - ); - }); - - it('filters react-native folder by default', () => { - const callback = jest.fn(); - readdirSync.mockImplementationOnce(() => [ - {name: 'react-native', isDirectory: () => true}, - ]); - - forEachPackage(callback); - - expect(callback).not.toHaveBeenCalled(); - }); - - it('includes react-native, if such option is provided', () => { - const callback = jest.fn(); - const mockedPackageManifest = '{"name": "react-native"}'; - const mockedParsedPackageManifest = JSON.parse(mockedPackageManifest); - const mockedPackageName = 'react-native'; - - readdirSync.mockImplementationOnce(() => [ - {name: 'react-native', isDirectory: () => true}, - ]); - readFileSync.mockImplementationOnce(() => mockedPackageManifest); - - forEachPackage(callback, {includeReactNative: true}); - - expect(callback).toHaveBeenCalledWith( - path.join(__dirname, '..', '..', '..', 'packages', mockedPackageName), - path.join('packages', mockedPackageName), - mockedParsedPackageManifest, - ); - }); -}); diff --git a/scripts/monorepo/align-package-versions.js b/scripts/monorepo/align-package-versions.js deleted file mode 100644 index d4ca5fc36b6cf4..00000000000000 --- a/scripts/monorepo/align-package-versions.js +++ /dev/null @@ -1,120 +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 - */ - -const forEachPackage = require('./for-each-package'); -const {readFileSync, writeFileSync} = require('fs'); -const path = require('path'); - -const ROOT_LOCATION = path.join(__dirname, '..', '..'); -const TEMPLATE_LOCATION = path.join( - ROOT_LOCATION, - 'packages', - 'react-native', - 'template', -); - -const readJSONFile = pathToFile => JSON.parse(readFileSync(pathToFile)); - -const checkIfShouldUpdateDependencyPackageVersion = ( - consumerPackageAbsolutePath, - updatedPackageName, - updatedPackageVersion, -) => { - const consumerPackageManifestPath = path.join( - consumerPackageAbsolutePath, - 'package.json', - ); - const consumerPackageManifest = readJSONFile(consumerPackageManifestPath); - - const dependencyVersion = - consumerPackageManifest.dependencies?.[updatedPackageName]; - - if (dependencyVersion && dependencyVersion !== '*') { - const updatedDependencyVersion = dependencyVersion.startsWith('^') - ? `^${updatedPackageVersion}` - : updatedPackageVersion; - - if (updatedDependencyVersion !== dependencyVersion) { - console.log( - `\uD83D\uDCA1 ${consumerPackageManifest.name} was updated: now using version ${updatedPackageVersion} of ${updatedPackageName}`, - ); - - const updatedPackageManifest = { - ...consumerPackageManifest, - dependencies: { - ...consumerPackageManifest.dependencies, - [updatedPackageName]: updatedDependencyVersion, - }, - }; - - writeFileSync( - consumerPackageManifestPath, - JSON.stringify(updatedPackageManifest, null, 2) + '\n', - 'utf-8', - ); - } - } - - const devDependencyVersion = - consumerPackageManifest.devDependencies?.[updatedPackageName]; - - if (devDependencyVersion && devDependencyVersion !== '*') { - const updatedDependencyVersion = devDependencyVersion.startsWith('^') - ? `^${updatedPackageVersion}` - : updatedPackageVersion; - - if (updatedDependencyVersion !== devDependencyVersion) { - console.log( - `\uD83D\uDCA1 ${consumerPackageManifest.name} was updated: now using version ${updatedPackageVersion} of ${updatedPackageName}`, - ); - - const updatedPackageManifest = { - ...consumerPackageManifest, - devDependencies: { - ...consumerPackageManifest.devDependencies, - [updatedPackageName]: updatedDependencyVersion, - }, - }; - - writeFileSync( - consumerPackageManifestPath, - JSON.stringify(updatedPackageManifest, null, 2) + '\n', - 'utf-8', - ); - } - } -}; - -const alignPackageVersions = () => { - forEachPackage((_, __, packageManifest) => { - checkIfShouldUpdateDependencyPackageVersion( - ROOT_LOCATION, - packageManifest.name, - packageManifest.version, - ); - - checkIfShouldUpdateDependencyPackageVersion( - TEMPLATE_LOCATION, - packageManifest.name, - packageManifest.version, - ); - - forEachPackage( - pathToPackage => - checkIfShouldUpdateDependencyPackageVersion( - pathToPackage, - packageManifest.name, - packageManifest.version, - ), - {includeReactNative: true}, - ); - }); -}; - -module.exports = alignPackageVersions; diff --git a/scripts/monorepo/bump-all-updated-packages/bump-package-version.js b/scripts/monorepo/bump-all-updated-packages/bump-package-version.js deleted file mode 100644 index d403eb9f94e712..00000000000000 --- a/scripts/monorepo/bump-all-updated-packages/bump-package-version.js +++ /dev/null @@ -1,52 +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 - */ - -const {writeFileSync} = require('fs'); -const path = require('path'); - -const getIncrementedVersion = (version, increment) => - version - .split('.') - .map((token, index) => { - const indexOfVersionToIncrement = increment === 'minor' ? 1 : 2; - - if (index === indexOfVersionToIncrement) { - return parseInt(token, 10) + 1; - } - - if (index > indexOfVersionToIncrement) { - return 0; - } - - return token; - }) - .join('.'); - -const bumpPackageVersion = ( - packageAbsolutePath, - packageManifest, - increment = 'patch', -) => { - const updatedVersion = getIncrementedVersion( - packageManifest.version, - increment, - ); - - // Not using simple `npm version patch` because it updates dependencies and yarn.lock file - writeFileSync( - path.join(packageAbsolutePath, 'package.json'), - JSON.stringify({...packageManifest, version: updatedVersion}, null, 2) + - '\n', - 'utf-8', - ); - - return updatedVersion; -}; - -module.exports = bumpPackageVersion; diff --git a/scripts/monorepo/bump-all-updated-packages/bump-utils.js b/scripts/monorepo/bump-all-updated-packages/bump-utils.js deleted file mode 100644 index 07c57e5313ab27..00000000000000 --- a/scripts/monorepo/bump-all-updated-packages/bump-utils.js +++ /dev/null @@ -1,49 +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 - */ - -const chalk = require('chalk'); -const {echo, exec} = require('shelljs'); - -const detectPackageUnreleasedChanges = ( - packageRelativePathFromRoot, - packageName, - ROOT_LOCATION, -) => { - const hashOfLastCommitInsidePackage = exec( - `git log -n 1 --format=format:%H -- ${packageRelativePathFromRoot}`, - {cwd: ROOT_LOCATION, silent: true}, - ).stdout.trim(); - - const hashOfLastCommitThatChangedVersion = exec( - `git log -G\\"version\\": --format=format:%H -n 1 -- ${packageRelativePathFromRoot}/package.json`, - {cwd: ROOT_LOCATION, silent: true}, - ).stdout.trim(); - - if (hashOfLastCommitInsidePackage === hashOfLastCommitThatChangedVersion) { - echo( - `\uD83D\uDD0E No changes for package ${chalk.green( - packageName, - )} since last version bump`, - ); - return false; - } else { - echo(`\uD83D\uDCA1 Found changes for ${chalk.yellow(packageName)}:`); - exec( - `git log --pretty=oneline ${hashOfLastCommitThatChangedVersion}..${hashOfLastCommitInsidePackage} ${packageRelativePathFromRoot}`, - { - cwd: ROOT_LOCATION, - }, - ); - echo(); - - return true; - } -}; - -module.exports = detectPackageUnreleasedChanges; diff --git a/scripts/monorepo/bump-all-updated-packages/index.js b/scripts/monorepo/bump-all-updated-packages/index.js deleted file mode 100644 index b619c3129c4fbd..00000000000000 --- a/scripts/monorepo/bump-all-updated-packages/index.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - * @format - * @oncall react_native - */ - -const {getPackageVersionStrByTag} = require('../../npm-utils'); -const { - isReleaseBranch, - parseVersion, -} = require('../../releases/utils/version-utils'); -const {getBranchName} = require('../../scm-utils'); -const alignPackageVersions = require('../align-package-versions'); -const checkForGitChanges = require('../check-for-git-changes'); -const { - COMMIT_WITH_CUSTOM_MESSAGE_CHOICE, - COMMIT_WITH_GENERIC_MESSAGE_CHOICE, - GENERIC_COMMIT_MESSAGE, - NO_COMMIT_CHOICE, - PUBLISH_PACKAGES_TAG, -} = require('../constants'); -const forEachPackage = require('../for-each-package'); -const bumpPackageVersion = require('./bump-package-version'); -const detectPackageUnreleasedChanges = require('./bump-utils'); -const chalk = require('chalk'); -const {execSync} = require('child_process'); -const inquirer = require('inquirer'); -const path = require('path'); -const {echo, exec, exit} = require('shelljs'); - -const ROOT_LOCATION = path.join(__dirname, '..', '..', '..'); - -const buildExecutor = - ( - packageAbsolutePath /*: string */, - packageRelativePathFromRoot /*: string */, - packageManifest /*: $FlowFixMe */, - ) => - async () => { - const {name: packageName} = packageManifest; - if (packageManifest.private) { - echo(`\u23ED Skipping private package ${chalk.dim(packageName)}`); - - return; - } - - if ( - !detectPackageUnreleasedChanges( - packageRelativePathFromRoot, - packageName, - ROOT_LOCATION, - ) - ) { - return; - } - - await inquirer - .prompt([ - { - type: 'list', - name: 'shouldBumpPackage', - message: `Do you want to bump ${packageName}?`, - choices: ['Yes', 'No'], - filter: val => val === 'Yes', - }, - ]) - .then(({shouldBumpPackage}) => { - if (!shouldBumpPackage) { - echo(`Skipping bump for ${packageName}`); - return; - } - - return inquirer - .prompt([ - { - type: 'list', - name: 'increment', - message: 'Which version you want to increment?', - choices: ['patch', 'minor'], - }, - ]) - .then(({increment}) => { - const updatedVersion = bumpPackageVersion( - packageAbsolutePath, - packageManifest, - increment, - ); - echo( - `\u2705 Successfully bumped ${chalk.green( - packageName, - )} to ${chalk.green(updatedVersion)}`, - ); - }); - }); - }; - -const buildAllExecutors = () => { - const executors = []; - - forEachPackage((...params) => { - executors.push(buildExecutor(...params)); - }); - - return executors; -}; - -const main = async () => { - if (checkForGitChanges()) { - echo( - chalk.red( - 'Found uncommitted changes. Please commit or stash them before running this script', - ), - ); - exit(1); - } - - const executors = buildAllExecutors(); - for (const executor of executors) { - await executor() - .catch(() => exit(1)) - .then(() => echo()); - } - - if (!checkForGitChanges()) { - echo('No changes have been made. Finishing the process...'); - exit(0); - } - - echo('Aligning new versions across monorepo...'); - alignPackageVersions(); - echo(chalk.green('Done!\n')); - - // Figure out the npm dist-tags we want for all monorepo packages we're bumping - const branchName = getBranchName(); - const choices = []; - - if (branchName === 'main') { - choices.push({name: '"nightly"', value: 'nightly', checked: true}); - } else if (isReleaseBranch(branchName)) { - choices.push({ - name: `"${branchName}"`, - value: branchName, - checked: true, - }); - - const latestVersion = getPackageVersionStrByTag('react-native', 'latest'); - const {major, minor} = parseVersion(latestVersion, 'release'); - choices.push({ - name: '"latest"', - value: 'latest', - checked: `${major}.${minor}-stable` === branchName, - }); - } else { - echo( - 'You should be running `yarn bump-all-updated-packages` only from release or main branch', - ); - exit(1); - } - - const {tags} = await inquirer.prompt([ - { - type: 'checkbox', - name: 'tags', - message: 'Select suggested npm tags.', - choices, - required: true, - }, - ]); - - const {confirm} = await inquirer.prompt({ - type: 'confirm', - name: 'confirm', - message: `Confirm these tags for *ALL* packages being bumped: ${tags - .map(t => `"${t}"`) - .join()}`, - }); - - if (!confirm) { - echo('Exiting without commiting...'); - exit(0); - } - - const tagString = '&' + tags.join('&'); - - await inquirer - .prompt([ - { - type: 'list', - name: 'commitChoice', - message: 'Do you want to submit a commit with these changes?', - choices: [ - { - name: 'Yes, with generic message', - value: COMMIT_WITH_GENERIC_MESSAGE_CHOICE, - }, - { - name: 'Yes, with custom message', - value: COMMIT_WITH_CUSTOM_MESSAGE_CHOICE, - }, - { - name: 'No', - value: NO_COMMIT_CHOICE, - }, - ], - }, - ]) - .then(({commitChoice}) => { - switch (commitChoice) { - case NO_COMMIT_CHOICE: { - echo('Not submitting a commit, but keeping all changes'); - - break; - } - - case COMMIT_WITH_GENERIC_MESSAGE_CHOICE: { - exec(`git commit -am "${GENERIC_COMMIT_MESSAGE}${tagString}"`, { - cwd: ROOT_LOCATION, - silent: true, - }); - - break; - } - - case COMMIT_WITH_CUSTOM_MESSAGE_CHOICE: { - // exec from shelljs currently does not support interactive input - // https://github.com/shelljs/shelljs/wiki/FAQ#running-interactive-programs-with-exec - execSync('git commit -a', {cwd: ROOT_LOCATION, stdio: 'inherit'}); - - const enteredCommitMessage = exec('git log -n 1 --format=format:%B', { - cwd: ROOT_LOCATION, - silent: true, - }).stdout.trim(); - const commitMessageWithTag = - enteredCommitMessage + `\n\n${PUBLISH_PACKAGES_TAG}${tagString}`; - - exec(`git commit --amend -m "${commitMessageWithTag}"`, { - cwd: ROOT_LOCATION, - silent: true, - }); - - break; - } - - default: - throw new Error(''); - } - }) - .then(() => echo()); - - echo(chalk.green('Successfully finished the process of bumping packages')); - exit(0); -}; - -if (require.main === module) { - // eslint-disable-next-line no-void - void main(); -} diff --git a/scripts/monorepo/constants.js b/scripts/monorepo/constants.js deleted file mode 100644 index 8dd1f6c5fe87a9..00000000000000 --- a/scripts/monorepo/constants.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -const PUBLISH_PACKAGES_TAG = '#publish-packages-to-npm'; -const GENERIC_COMMIT_MESSAGE = `bumped packages versions\n\n${PUBLISH_PACKAGES_TAG}`; - -const NO_COMMIT_CHOICE = 'NO_COMMIT'; -const COMMIT_WITH_GENERIC_MESSAGE_CHOICE = 'COMMIT_WITH_GENERIC_MESSAGE'; -const COMMIT_WITH_CUSTOM_MESSAGE_CHOICE = 'COMMIT_WITH_CUSTOM_MESSAGE'; - -module.exports = { - PUBLISH_PACKAGES_TAG, - GENERIC_COMMIT_MESSAGE, - NO_COMMIT_CHOICE, - COMMIT_WITH_GENERIC_MESSAGE_CHOICE, - COMMIT_WITH_CUSTOM_MESSAGE_CHOICE, -}; diff --git a/scripts/monorepo/for-each-package.js b/scripts/monorepo/for-each-package.js deleted file mode 100644 index 49d7c9eee3f028..00000000000000 --- a/scripts/monorepo/for-each-package.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -const {PACKAGES_DIR} = require('../consts'); -const {readdirSync, readFileSync} = require('fs'); -const path = require('path'); - -const DEFAULT_OPTIONS /*: Options */ = {includeReactNative: false}; - -/*:: -type PackageJSON = { - name: string, - private?: ?boolean, - version: string, - dependencies: {[string]: string}, - devDependencies: {[string]: string}, - ... -}; - -type Options = { - includeReactNative?: ?boolean, -}; -*/ - -/** - * Function, which returns an array of all directories inside specified location - */ -const getDirectories = (source /*: string */) /*: Array */ => - readdirSync(source, {withFileTypes: true}) - .filter(file => file.isDirectory()) - .map(directory => directory.name.toString()); -/** - * Iterate through every package inside /packages (ignoring react-native) and call provided callback for each of them - * - * @deprecated Use scripts/releases/utils/monorepo.js#getPackages instead - */ -const forEachPackage = ( - callback /*: (string, string, PackageJSON) => void */, - options /*: Options */ = DEFAULT_OPTIONS, -) => { - const {includeReactNative} = options; - - // We filter react-native package on purpose, so that no CI's script will be executed for this package in future - // Unless includeReactNative options is provided - const packagesDirectories = getDirectories(PACKAGES_DIR).filter( - directoryName => - directoryName !== 'react-native' || includeReactNative === true, - ); - - packagesDirectories.forEach(packageDirectory => { - const packageAbsolutePath = path.join(PACKAGES_DIR, packageDirectory); - const packageRelativePathFromRoot = path.join('packages', packageDirectory); - - const packageManifest = JSON.parse( - readFileSync(path.join(packageAbsolutePath, 'package.json')).toString(), - ); - - callback(packageAbsolutePath, packageRelativePathFromRoot, packageManifest); - }); -}; - -module.exports = forEachPackage; diff --git a/scripts/monorepo/print/index.js b/scripts/monorepo/print/index.js index 5846837ddfaf02..4783c5c02f37b6 100644 --- a/scripts/monorepo/print/index.js +++ b/scripts/monorepo/print/index.js @@ -8,7 +8,7 @@ */ const {getVersionsBySpec} = require('../../npm-utils'); -const forEachPackage = require('../for-each-package'); +const {getPackages} = require('../../utils/monorepo'); const {exit} = require('shelljs'); const yargs = require('yargs'); @@ -41,40 +41,46 @@ function reversePatchComp(semverA, semverB) { return patchB - patchA; } -const main = () => { +async function main() { const data = []; - forEachPackage( - (_packageAbsolutePath, _packageRelativePathFromRoot, packageManifest) => { - const isPublic = !packageManifest.private; - if ( - type === 'all' || - (type === 'private' && !isPublic) || - (type === 'public' && isPublic) - ) { - const packageInfo = { - 'Public?': isPublic ? '\u{2705}' : '\u{274C}', - Name: packageManifest.name, - 'Version (main)': packageManifest.version, - }; + const packages = await getPackages({ + includeReactNative: true, + includePrivate: true, + }); - if (isPublic && minor !== 0) { - try { - const versions = getVersionsBySpec( - packageManifest.name, - `^0.${minor}.0`, - ).sort(reversePatchComp); - packageInfo[`Version (${minor})`] = versions[0]; - } catch (e) { - packageInfo[`Version (${minor})`] = e.message; - } + for (const {packageJson} of Object.values(packages)) { + const isPublic = !packageJson.private; + if ( + type === 'all' || + (type === 'private' && !isPublic) || + (type === 'public' && isPublic) + ) { + const packageInfo = { + 'Public?': isPublic ? '\u{2705}' : '\u{274C}', + Name: packageJson.name, + 'Version (main)': packageJson.version, + }; + + if (isPublic && minor !== 0) { + try { + const versions = getVersionsBySpec( + packageJson.name, + `^0.${minor}.0`, + ).sort(reversePatchComp); + packageInfo[`Version (${minor})`] = versions[0]; + } catch (e) { + packageInfo[`Version (${minor})`] = e.message; } - data.push(packageInfo); } - }, - {includeReactNative: true}, - ); + data.push(packageInfo); + } + } + console.table(data); exit(0); -}; +} -main(); +if (require.main === module) { + // eslint-disable-next-line no-void + void main(); +} diff --git a/scripts/npm-utils.js b/scripts/npm-utils.js index b127448c6856b0..de9198bc567e10 100644 --- a/scripts/npm-utils.js +++ b/scripts/npm-utils.js @@ -94,7 +94,7 @@ function getNpmInfo(buildType /*: BuildType */) /*: NpmInfo */ { ); } - const {version, major, minor, prerelease} = parseVersion( + const {version, major, minor, patch, prerelease} = parseVersion( process.env.CIRCLE_TAG, buildType, ); @@ -107,15 +107,19 @@ function getNpmInfo(buildType /*: BuildType */) /*: NpmInfo */ { ); const releaseBranchTag = `${major}.${minor}-stable`; - + let tag = releaseBranchTag; // npm will automatically tag the version as `latest` if no tag is set when we publish // To prevent this, use `releaseBranchTag` when we don't want that (ex. releasing a patch on older release) - const tag = - prerelease != null - ? 'next' - : isLatest === true - ? 'latest' - : releaseBranchTag; + if (prerelease != null) { + if (patch === '0') { + // Set `next` tag only on prereleases of 0.m.0-RC.k. + tag = 'next'; + } else { + tag = '--no-tag'; + } + } else if (isLatest === true) { + tag = 'latest'; + } return { version, @@ -132,7 +136,17 @@ function publishPackage( execOptions /*: ?ExecOptsSync */, ) /*: ShellString */ { const {otp, tags} = packageOptions; - const tagsFlag = tags != null ? tags.map(t => ` --tag ${t}`).join('') : ''; + + let tagsFlag = ''; + if (tags != null) { + tagsFlag = tags.includes('--no-tag') + ? ' --no-tag' + : tags + .filter(Boolean) + .map(t => ` --tag ${t}`) + .join(''); + } + const otpFlag = otp != null ? ` --otp ${otp}` : ''; const options = execOptions ? {...execOptions, cwd: packagePath} @@ -141,35 +155,6 @@ function publishPackage( return exec(`npm publish${tagsFlag}${otpFlag}`, options); } -function diffPackages( - packageSpecA /*: string */, - packageSpecB /*: string */, - options /*: ExecOptsSync */, -) /*: string */ { - const result = exec( - `npm diff --diff=${packageSpecA} --diff=${packageSpecB} --diff-name-only`, - options, - ); - - if (result.code !== 0) { - throw new Error( - `Failed to diff ${packageSpecA} and ${packageSpecB}\n${result.stderr}`, - ); - } - - return result.stdout; -} - -function pack(packagePath /*: string */) { - const result = exec('npm pack', { - cwd: packagePath, - }); - - if (result.code !== 0) { - throw new Error(result.stderr); - } -} - /** * `package` is an object form of package.json * `dependencies` is a map of dependency to version string @@ -272,9 +257,6 @@ function getVersionsBySpec( module.exports = { applyPackageVersions, getNpmInfo, - getPackageVersionStrByTag, getVersionsBySpec, publishPackage, - diffPackages, - pack, }; diff --git a/scripts/release-testing/test-e2e-local.js b/scripts/release-testing/test-e2e-local.js index fbdd376f291a77..01efa244a5bb3e 100644 --- a/scripts/release-testing/test-e2e-local.js +++ b/scripts/release-testing/test-e2e-local.js @@ -27,6 +27,7 @@ const { prepareArtifacts, setupCircleCIArtifacts, } = require('./utils/testing-utils'); +const chalk = require('chalk'); const debug = require('debug')('test-e2e-local'); const fs = require('fs'); const path = require('path'); @@ -301,10 +302,10 @@ async function testRNTestProject( ); cd('..'); - exec('yarn ios'); + exec('npm run ios'); } else { // android - exec('yarn android'); + exec('npm run android'); } popd(); } @@ -329,7 +330,7 @@ async function main() { let circleCIArtifacts = await setupCircleCIArtifacts( // $FlowIgnoreError[prop-missing] - argv.circleCIToken, + argv.circleciToken, branchName, ); @@ -337,6 +338,15 @@ async function main() { await testRNTester(circleCIArtifacts, onReleaseBranch); } else { await testRNTestProject(circleCIArtifacts); + + console.warn( + chalk.yellow(` +================================================================================ +NOTE: Verdaccio may still be running on after this script has finished. Please +Force Quit via Activity Monitor. +================================================================================ + `), + ); } } diff --git a/scripts/releases-ci/__tests__/publish-npm-test.js b/scripts/releases-ci/__tests__/publish-npm-test.js index c8852d37a50179..3dd4230b14f168 100644 --- a/scripts/releases-ci/__tests__/publish-npm-test.js +++ b/scripts/releases-ci/__tests__/publish-npm-test.js @@ -121,14 +121,14 @@ describe('publish-npm', () => { describe("publishNpm('nightly')", () => { beforeAll(() => { - jest.mock('../../releases/utils/monorepo', () => ({ - ...jest.requireActual('../../releases/utils/monorepo'), + jest.mock('../../utils/monorepo', () => ({ + ...jest.requireActual('../../utils/monorepo'), getPackages: getPackagesMock, })); }); afterAll(() => { - jest.unmock('../../releases/utils/monorepo'); + jest.unmock('../../utils/monorepo'); }); it('should publish', async () => { diff --git a/scripts/releases-ci/__tests__/publish-updated-packages-test.js b/scripts/releases-ci/__tests__/publish-updated-packages-test.js index 9f7845ba93e10d..c2b1918c819a53 100644 --- a/scripts/releases-ci/__tests__/publish-updated-packages-test.js +++ b/scripts/releases-ci/__tests__/publish-updated-packages-test.js @@ -21,7 +21,7 @@ const fetchMock = jest.fn(); jest.mock('child_process', () => ({execSync})); jest.mock('shelljs', () => ({exec: execMock})); -jest.mock('../../releases/utils/monorepo', () => ({ +jest.mock('../../utils/monorepo', () => ({ getPackages: getPackagesMock, })); // $FlowIgnore[cannot-write] diff --git a/scripts/releases-ci/prepare-package-for-release.js b/scripts/releases-ci/prepare-package-for-release.js deleted file mode 100755 index 7969b8b1ccd566..00000000000000 --- a/scripts/releases-ci/prepare-package-for-release.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - * @format - * @oncall react_native - */ - -'use strict'; - -const {setReactNativeVersion} = require('../releases/set-rn-version'); -const {failIfTagExists} = require('../releases/utils/release-utils'); -const { - isReleaseBranch, - parseVersion, -} = require('../releases/utils/version-utils'); -const {echo, exec, exit} = require('shelljs'); -const yargs = require('yargs'); - -/** - * This script prepares a release package to be pushed to npm - * It is triggered to run on CircleCI - * It will: - * * It updates the version in json/gradle files and makes sure they are consistent between each other (set-rn-version) - * * Updates podfile for RNTester - * * Commits changes and tags with the next version based off of last version tag. - * This in turn will trigger another CircleCI job to publish to npm - */ -async function main() { - const argv = yargs - .option('r', { - alias: 'remote', - default: 'origin', - }) - .option('v', { - alias: 'to-version', - type: 'string', - required: true, - }) - .option('l', { - alias: 'latest', - type: 'boolean', - default: false, - }) - .option('d', { - alias: 'dry-run', - type: 'boolean', - default: false, - }).argv; - - const branch = process.env.CIRCLE_BRANCH; - // $FlowFixMe[prop-missing] - const remote = argv.remote; - // $FlowFixMe[prop-missing] - const releaseVersion = argv.toVersion; - // $FlowFixMe[prop-missing] - const isLatest = argv.latest; - // $FlowFixMe[prop-missing] - const isDryRun = argv.dryRun; - - if (branch == null) { - throw new Error('process.env.CIRCLE_BRANCH is not set'); - } - - const buildType = isDryRun - ? 'dry-run' - : isReleaseBranch(branch) - ? 'release' - : 'nightly'; - - failIfTagExists(releaseVersion, buildType); - - if (branch && !isReleaseBranch(branch) && !isDryRun) { - console.error(`This needs to be on a release branch. On branch: ${branch}`); - exit(1); - } else if (!branch && !isDryRun) { - console.error('This needs to be on a release branch.'); - exit(1); - } - - const {version} = parseVersion(releaseVersion, buildType); - if (version == null) { - console.error(`Invalid version provided: ${releaseVersion}`); - exit(1); - } - - try { - await setReactNativeVersion(version, null, buildType); - } catch (e) { - echo(`Failed to set React Native version to ${version}`); - exit(1); - } - - // Release builds should commit the version bumps, and create tags. - echo('Updating RNTester Podfile.lock...'); - if (exec('source scripts/update_podfile_lock.sh && update_pods').code) { - echo('Failed to update RNTester Podfile.lock.'); - echo('Fix the issue, revert and try again.'); - exit(1); - } - - echo(`Local checkout has been prepared for release version ${version}.`); - if (isDryRun) { - echo('Changes will not be committed because --dry-run was set to true.'); - exit(0); - } - - // Make commit [0.21.0-rc] Bump version numbers - if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) { - echo('failed to commit'); - exit(1); - } - - // Add tag v0.21.0-rc.1 - if (exec(`git tag -a v${version} -m "v${version}"`).code) { - echo( - `failed to tag the commit with v${version}, are you sure this release wasn't made earlier?`, - ); - echo('You may want to rollback the last commit'); - echo('git reset --hard HEAD~1'); - exit(1); - } - - // If `isLatest`, this git tag will also set npm release as `latest` - if (isLatest) { - exec('git tag -d latest'); - exec(`git push ${remote} :latest`); - - // This will be pushed with the `--follow-tags` - exec('git tag -a latest -m "latest"'); - } - - exec(`git push ${remote} ${branch} --follow-tags`); -} - -if (require.main === module) { - // eslint-disable-next-line no-void - void main(); -} diff --git a/scripts/releases-ci/publish-npm.js b/scripts/releases-ci/publish-npm.js index ddb38248f96b1e..26933699cb900d 100755 --- a/scripts/releases-ci/publish-npm.js +++ b/scripts/releases-ci/publish-npm.js @@ -20,11 +20,11 @@ const {getNpmInfo, publishPackage} = require('../npm-utils'); const {removeNewArchFlags} = require('../releases/remove-new-arch-flags'); const {setReactNativeVersion} = require('../releases/set-rn-version'); const setVersion = require('../releases/set-version'); -const {getPackages} = require('../releases/utils/monorepo'); const { generateAndroidArtifacts, publishAndroidArtifactsToMaven, } = require('../releases/utils/release-utils'); +const {getPackages} = require('../utils/monorepo'); const path = require('path'); const yargs = require('yargs'); diff --git a/scripts/releases-ci/publish-updated-packages.js b/scripts/releases-ci/publish-updated-packages.js index f20a7d5b45bf95..ff5bf6c688dbe0 100644 --- a/scripts/releases-ci/publish-updated-packages.js +++ b/scripts/releases-ci/publish-updated-packages.js @@ -9,12 +9,12 @@ * @oncall react_native */ -const {PUBLISH_PACKAGES_TAG} = require('../monorepo/constants'); const {publishPackage} = require('../npm-utils'); -const {getPackages} = require('../releases/utils/monorepo'); +const {getPackages} = require('../utils/monorepo'); const {parseArgs} = require('@pkgjs/parseargs'); const {execSync} = require('child_process'); +const PUBLISH_PACKAGES_TAG = '#publish-packages-to-npm'; const NPM_CONFIG_OTP = process.env.NPM_CONFIG_OTP; const config = { diff --git a/scripts/releases-local/trigger-react-native-release.js b/scripts/releases-local/trigger-react-native-release.js index 266c5cccc3e1fc..154baa20ddcba8 100644 --- a/scripts/releases-local/trigger-react-native-release.js +++ b/scripts/releases-local/trigger-react-native-release.js @@ -11,16 +11,14 @@ 'use strict'; -const {REPO_ROOT} = require('../consts'); -const detectPackageUnreleasedChanges = require('../monorepo/bump-all-updated-packages/bump-utils.js'); const checkForGitChanges = require('../monorepo/check-for-git-changes'); -const forEachPackage = require('../monorepo/for-each-package'); const {failIfTagExists} = require('../releases/utils/release-utils'); const { isReleaseBranch, parseVersion, } = require('../releases/utils/version-utils'); const {exitIfNotOnGit, getBranchName} = require('../scm-utils'); +const {getPackages} = require('../utils/monorepo'); const chalk = require('chalk'); const inquirer = require('inquirer'); const request = require('request'); @@ -48,6 +46,10 @@ let argv = yargs describe: 'Version you aim to release, ex. 0.67.0-rc.1, 0.66.3', required: true, }) + .option('dry-run', { + type: 'boolean', + default: false, + }) .check(() => { const branch = exitIfNotOnGit( () => getBranchName(), @@ -66,54 +68,34 @@ function exitIfNotOnReleaseBranch(branch /*: string */) { } } -const buildExecutor = - ( - packageAbsolutePath /*: string */, - packageRelativePathFromRoot /*: string */, - packageManifest /*: $FlowFixMe */, - ) => - async () => { - const {name: packageName} = packageManifest; - if (packageManifest.private) { - return; - } - - if ( - detectPackageUnreleasedChanges( - packageRelativePathFromRoot, - packageName, - REPO_ROOT, - ) - ) { - // if I enter here, I want to throw an error upward - throw new Error( - `Package ${packageName} has unreleased changes. Please release it first.`, - ); - } - }; - -const buildAllExecutors = () => { - const executors = []; +/** + * Get the next version that all workspace packages will be set to. + * + * This approach is specific to the 0.74 release. For 0.75, the `--to-version` + * value will be used instead, setting all packages to a single version. + */ +async function getNextMonorepoPackagesVersion() /*: Promise */ { + // Based on last publish before this strategy + const _0_74_MIN_PATCH = 75; - forEachPackage((...params) => { - executors.push(buildExecutor(...params)); + const packages = await getPackages({ + includeReactNative: false, }); - return executors; -}; + let patchVersion = _0_74_MIN_PATCH; -async function exitIfUnreleasedPackages() { - // use the other script to verify that there's no packages in the monorepo - // that have changes that haven't been released + for (const pkg of Object.values(packages)) { + const {version} = pkg.packageJson; - const executors = buildAllExecutors(); - for (const executor of executors) { - await executor().catch(error => { - echo(chalk.red(error)); - // need to throw upward - throw error; - }); + if (!version.startsWith('0.74.') || version.endsWith('-main')) { + return null; + } + + const {patch} = parseVersion(version, 'release'); + patchVersion = Math.max(patchVersion, parseInt(patch, 10) + 1); } + + return '0.74.' + patchVersion; } function triggerReleaseWorkflow(options /*: $FlowFixMe */) { @@ -144,13 +126,6 @@ async function main() { exit(1); } - // now check for unreleased packages - try { - await exitIfUnreleasedPackages(); - } catch (error) { - exit(1); - } - // $FlowFixMe[prop-missing] const token = argv.token; // $FlowFixMe[prop-missing] @@ -171,7 +146,7 @@ async function main() { } let latest = false; - const {version, prerelease} = parseVersion(releaseVersion, 'release'); + const {version, patch, prerelease} = parseVersion(releaseVersion, 'release'); if (!prerelease) { const {setLatest} = await inquirer.prompt({ type: 'confirm', @@ -181,7 +156,14 @@ async function main() { latest = setLatest; } - const npmTag = latest ? 'latest' : !prerelease ? branch : 'next'; + const npmTag = latest + ? 'latest' + : !prerelease + ? branch + : patch === '0' + ? 'next' + : '--no-tag'; + const {confirmRelease} = await inquirer.prompt({ type: 'confirm', name: 'confirmRelease', @@ -193,10 +175,28 @@ async function main() { return; } + let nextMonorepoPackagesVersion = await getNextMonorepoPackagesVersion(); + + if (nextMonorepoPackagesVersion == null) { + // TODO(T182538198): Once this warning is hit, we can remove the + // `release_monorepo_packages_version` logic from here and the CI jobs, + // see other TODOs. + console.warn( + 'Warning: No longer on the 0.74-stable branch, meaning we will ' + + 'write all package versions identically. Please double-check the ' + + 'generated diff to see if this is correct.', + ); + nextMonorepoPackagesVersion = version; + } + const parameters = { - release_version: version, - release_latest: latest, run_release_workflow: true, + release_version: version, + release_tag: npmTag, + // NOTE: Necessary for 0.74, should be dropped for 0.75+ + release_monorepo_packages_version: nextMonorepoPackagesVersion, + // $FlowFixMe[prop-missing] + release_dry_run: argv.dryRun, }; const options = { diff --git a/scripts/releases/set-version/__tests__/__fixtures__/package.json b/scripts/releases/set-version/__tests__/__fixtures__/package.json new file mode 100644 index 00000000000000..f8a85180bf959f --- /dev/null +++ b/scripts/releases/set-version/__tests__/__fixtures__/package.json @@ -0,0 +1,8 @@ +{ + "name": "@react-native/monorepo", + "private": true, + "version": "1000.0.0", + "devDependencies": { + "@monorepo/pkg-c": "0.0.1" + } +} diff --git a/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap b/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap index 33c0979d209f4c..174df2705eea81 100644 --- a/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap +++ b/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap @@ -1,5 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`setVersion updates monorepo for nightly: package.json 1`] = ` +"{ + \\"name\\": \\"@react-native/monorepo\\", + \\"private\\": true, + \\"version\\": \\"1000.0.0\\", + \\"devDependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.81.0-nightly-29282302-abcd1234\\" + } +} +" +`; + exports[`setVersion updates monorepo for nightly: packages/monorepo-pkg-a/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-a\\", @@ -198,6 +210,18 @@ exports[`setVersion updates monorepo for nightly: packages/react-native/template " `; +exports[`setVersion updates monorepo for release-candidate: package.json 1`] = ` +"{ + \\"name\\": \\"@react-native/monorepo\\", + \\"private\\": true, + \\"version\\": \\"1000.0.0\\", + \\"devDependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.80.0-rc.3\\" + } +} +" +`; + exports[`setVersion updates monorepo for release-candidate: packages/monorepo-pkg-a/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-a\\", @@ -396,6 +420,18 @@ exports[`setVersion updates monorepo for release-candidate: packages/react-nativ " `; +exports[`setVersion updates monorepo for stable version: package.json 1`] = ` +"{ + \\"name\\": \\"@react-native/monorepo\\", + \\"private\\": true, + \\"version\\": \\"1000.0.0\\", + \\"devDependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.80.1\\" + } +} +" +`; + exports[`setVersion updates monorepo for stable version: packages/monorepo-pkg-a/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-a\\", @@ -594,6 +630,18 @@ exports[`setVersion updates monorepo for stable version: packages/react-native/t " `; +exports[`setVersion updates monorepo on main after release cut: package.json 1`] = ` +"{ + \\"name\\": \\"@react-native/monorepo\\", + \\"private\\": true, + \\"version\\": \\"1000.0.0\\", + \\"devDependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.82.0-main\\" + } +} +" +`; + exports[`setVersion updates monorepo on main after release cut: packages/monorepo-pkg-a/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-a\\", diff --git a/scripts/releases/set-version/index.js b/scripts/releases/set-version/index.js index 443863565dc383..f986487e19802b 100644 --- a/scripts/releases/set-version/index.js +++ b/scripts/releases/set-version/index.js @@ -12,11 +12,11 @@ 'use strict'; /*:: -import type {PackageJson} from '../utils/monorepo'; +import type {PackageJson} from '../../utils/monorepo'; */ +const {getPackages, getWorkspaceRoot} = require('../../utils/monorepo'); const {setReactNativeVersion} = require('../set-rn-version'); -const {getPackages} = require('../utils/monorepo'); const {promises: fs} = require('fs'); const path = require('path'); const yargs = require('yargs'); @@ -61,7 +61,7 @@ async function updatePackageJson( * stays 1000.0.0. * * This script does the following: - * - Update all public npm packages under `/packages` to specified version + * - Update all packages under `/packages` to specified version * - Update all npm dependencies of a `/packages` package to specified version * - Update npm dependencies of the template app (`packages/react-native/template`) to specified version * - Update `packages/react-native` native source and build files to specified version if relevant @@ -71,7 +71,7 @@ async function setVersion( skipReactNativeVersion /*: boolean */ = false, ) /*: Promise */ { const packages = await getPackages({ - includePrivate: false, + includePrivate: true, includeReactNative: true, }); const newPackageVersions = Object.fromEntries( @@ -83,11 +83,12 @@ async function setVersion( newPackageVersions, ); - // Exclude the react-native package, since this (and the template) are - // handled by `setReactNativeVersion`. - const packagesToUpdate = Object.values(packages).filter( - pkg => pkg.name !== 'react-native', - ); + const packagesToUpdate = [ + await getWorkspaceRoot(), + // Exclude the react-native package, since this (and the template) are + // handled by `setReactNativeVersion`. + ...Object.values(packages).filter(pkg => pkg.name !== 'react-native'), + ]; await Promise.all( packagesToUpdate.map(({path: packagePath, packageJson}) => diff --git a/scripts/releases/utils/__tests__/version-utils-test.js b/scripts/releases/utils/__tests__/version-utils-test.js index 044619b31e1580..c20328206a44a7 100644 --- a/scripts/releases/utils/__tests__/version-utils-test.js +++ b/scripts/releases/utils/__tests__/version-utils-test.js @@ -135,6 +135,18 @@ describe('version-utils', () => { expect(prerelease).toBe('rc.4'); }); + it('should parse patch pre-release version from tag', () => { + const {version, major, minor, patch, prerelease} = parseVersion( + 'v0.66.1-rc.4', + 'release', + ); + expect(version).toBe('0.66.1-rc.4'); + expect(major).toBe('0'); + expect(minor).toBe('66'); + expect(patch).toBe('1'); + expect(prerelease).toBe('rc.4'); + }); + it('should reject pre-release version from tag with random prerelease pattern', () => { function testInvalidVersion() { parseVersion('v0.66.0-something_invalid', 'release'); diff --git a/scripts/releases/utils/version-utils.js b/scripts/releases/utils/version-utils.js index e37c74e0036121..11f79318cb47d3 100644 --- a/scripts/releases/utils/version-utils.js +++ b/scripts/releases/utils/version-utils.js @@ -29,7 +29,7 @@ export type Version = { * * Some examples of valid versions are: * - stable: 0.68.1 - * - stable prerelease: 0.70.0-rc.0 + * - prerelease: 0.Y.Z-rc.K * - e2e-test: X.Y.Z-20221116-2018 * - nightly: X.Y.Z-20221116-0bc4547fc * - dryrun: 1000.0.0 @@ -135,14 +135,17 @@ function validatePrealpha(version /*: Version */) { function isStableRelease(version /*: Version */) /*: boolean */ { return ( - version.major === '0' && version.minor !== '0' && version.prerelease == null + version.major === '0' && + !!version.minor.match(/^\d+$/) && + !!version.patch.match(/^\d+$/) && + version.prerelease == null ); } function isStablePrerelease(version /*: Version */) /*: boolean */ { return !!( version.major === '0' && - version.minor !== '0' && + version.minor.match(/^\d+$/) && version.patch.match(/^\d+$/) && (version.prerelease?.startsWith('rc.') || version.prerelease?.startsWith('rc-') || diff --git a/scripts/update_podfile_lock.sh b/scripts/update_podfile_lock.sh deleted file mode 100755 index 4a65df8c9d909a..00000000000000 --- a/scripts/update_podfile_lock.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# 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. - -# This script updates RNTester Podfile.lock after verifying the CocoaPods environment. -# Usage: -# source scripts/update_podfile_lock && update_pods - -THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -RNTESTER_DIR="$THIS_DIR/../packages/rn-tester" - -# Keep this separate for FB internal access. -validate_env () { - cd "$RNTESTER_DIR" || exit - bundle check || exit - cd "$THIS_DIR" || exit -} - -update_pods () { - cd "$RNTESTER_DIR" || exit - bundle install || exit - bundle check || exit - bundle exec pod install - cd "$THIS_DIR" || exit -} diff --git a/scripts/releases/utils/monorepo.js b/scripts/utils/monorepo.js similarity index 65% rename from scripts/releases/utils/monorepo.js rename to scripts/utils/monorepo.js index 4908f951e8ee3a..304ee9c37c2ec1 100644 --- a/scripts/releases/utils/monorepo.js +++ b/scripts/utils/monorepo.js @@ -9,7 +9,7 @@ * @oncall react_native */ -const {REPO_ROOT} = require('../../consts'); +const {REPO_ROOT} = require('../consts'); const fs = require('fs'); const glob = require('glob'); const path = require('path'); @@ -65,30 +65,46 @@ async function getPackages( ? [] : ['packages/react-native/package.json'], }) - .map(async packageJsonPath => { - const packagePath = path.dirname(packageJsonPath); - const packageJson = JSON.parse( - await fs.promises.readFile(packageJsonPath, 'utf-8'), - ); - - return [ - packageJson.name, - { - name: packageJson.name, - path: packagePath, - packageJson, - }, - ]; - }), + .map(parsePackageInfo), ); return Object.fromEntries( packagesEntries.filter( - ([_, {packageJson}]) => !packageJson.private || includePrivate, + ([_, {packageJson}]) => packageJson.private !== true || includePrivate, ), ); } +/** + * Get the parsed package metadata for the workspace root. + */ +async function getWorkspaceRoot() /*: Promise */ { + const [, packageInfo] = await parsePackageInfo( + path.join(REPO_ROOT, 'package.json'), + ); + + return packageInfo; +} + +async function parsePackageInfo( + packageJsonPath /*: string */, +) /*: Promise<[string, PackageInfo]> */ { + const packagePath = path.dirname(packageJsonPath); + const packageJson /*: PackageJson */ = JSON.parse( + await fs.promises.readFile(packageJsonPath, 'utf-8'), + ); + + return [ + packageJson.name, + { + name: packageJson.name, + path: packagePath, + packageJson, + }, + ]; +} + module.exports = { getPackages, + getWorkspaceRoot, }; diff --git a/settings.gradle.kts b/settings.gradle.kts index 21064d004c9efe..0919ad1f7a74bf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ include( ":packages:react-native:ReactAndroid", ":packages:react-native:ReactAndroid:hermes-engine", ":packages:react-native:ReactAndroid:external-artifacts", + ":packages:react-native-popup-menu-android:android", ":packages:rn-tester:android:app") includeBuild("packages/react-native-gradle-plugin/") diff --git a/yarn.lock b/yarn.lock index 0437eb362646b0..b4415f08c5af53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2384,45 +2384,45 @@ optionalDependencies: npmlog "2 || ^3.1.0 || ^4.0.0" -"@react-native-community/cli-clean@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.0.tgz#64d205d10d40de23f87fc20d91a8221c886cd130" - integrity sha512-pIaPxvvqdROohjnxLYkE5CDiuJWYrpzWobVu10an6QJVR2AKpmKMwoH0v5bfZXUCd1DppaYyqTdvx8navakOAA== +"@react-native-community/cli-clean@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz#b6754f39c2b877c9d730feb848945150e1d52209" + integrity sha512-7Dj5+4p9JggxuVNOjPbduZBAP1SUgNhLKVw5noBUzT/3ZpUZkDM+RCSwyoyg8xKWoE4OrdUAXwAFlMcFDPKykA== dependencies: - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-tools" "13.6.9" chalk "^4.1.2" execa "^5.0.0" fast-glob "^3.3.2" -"@react-native-community/cli-config@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-13.6.0.tgz#a12eb3cf4799353eeb76b0ff662ad6013bf6ce36" - integrity sha512-KOesQvvntxgz0mT2uL+O7LrFNtA0y625FS1UdTplia9aJre3p8ZtHdyMfnXNp7ikbMcOTCmaMsH9GIqJUBswXg== +"@react-native-community/cli-config@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-13.6.9.tgz#d609a64d40a173c89bd7d24e31807bb7dcba69f9" + integrity sha512-rFfVBcNojcMm+KKHE/xqpqXg8HoKl4EC7bFHUrahMJ+y/tZll55+oX/PGG37rzB8QzP2UbMQ19DYQKC1G7kXeg== dependencies: - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-tools" "13.6.9" chalk "^4.1.2" cosmiconfig "^5.1.0" deepmerge "^4.3.0" fast-glob "^3.3.2" joi "^17.2.1" -"@react-native-community/cli-debugger-ui@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.0.tgz#a44effb910d084984c8dae608ff0a1344e0fca6f" - integrity sha512-w2Kr1HIcgBw1kNeSRp3lkQJeIAeVRfFNoCGN934NmtGUsex4iFf+VADGxo5f9EIF4t5zQSRz35AP5pcZfxMepg== +"@react-native-community/cli-debugger-ui@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.9.tgz#bc5727c51964206a00d417e5148b46331a81d5a5" + integrity sha512-TkN7IdFmGPPvTpAo3nCAH9uwGCPxWBEAwpqEZDrq0NWllI7Tdie8vDpGdrcuCcKalmhq6OYnkXzeBah7O1Ztpw== dependencies: serve-static "^1.13.1" -"@react-native-community/cli-doctor@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-13.6.0.tgz#e3749a5601a1baf7d01eb9e601b79d4c77693abc" - integrity sha512-2FnKYaiSkxiwrv7PkVT18HmwNJiPNFuD7xvAs6CM1+PlQX91Qukfw7+DWzVz1Jm4XB7WEWgZj/0xA0m7ic+5BQ== +"@react-native-community/cli-doctor@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-13.6.9.tgz#f1d4eeff427ddc8a9d19851042621c10939c35cb" + integrity sha512-5quFaLdWFQB+677GXh5dGU9I5eg2z6Vg4jOX9vKnc9IffwyIFAyJfCZHrxLSRPDGNXD7biDQUdoezXYGwb6P/A== dependencies: - "@react-native-community/cli-config" "13.6.0" - "@react-native-community/cli-platform-android" "13.6.0" - "@react-native-community/cli-platform-apple" "13.6.0" - "@react-native-community/cli-platform-ios" "13.6.0" - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-config" "13.6.9" + "@react-native-community/cli-platform-android" "13.6.9" + "@react-native-community/cli-platform-apple" "13.6.9" + "@react-native-community/cli-platform-ios" "13.6.9" + "@react-native-community/cli-tools" "13.6.9" chalk "^4.1.2" command-exists "^1.2.8" deepmerge "^4.3.0" @@ -2436,66 +2436,66 @@ wcwidth "^1.0.1" yaml "^2.2.1" -"@react-native-community/cli-hermes@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-13.6.0.tgz#af0a5baa33e6d5d4945d1b9022caaa19ae42dae3" - integrity sha512-PkzkB8gJ09UCJsmC5tqMnU8fgBOLiU0HI7uj2axtYLCj4IJ54J7ojRaXvisBUgDYv/yui7hPvuBIfqlA4vkowQ== +"@react-native-community/cli-hermes@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-13.6.9.tgz#88c8dfe936a0d4272efc54429eda9ccc3fca3ad8" + integrity sha512-GvwiwgvFw4Ws+krg2+gYj8sR3g05evmNjAHkKIKMkDTJjZ8EdyxbkifRUs1ZCq3TMZy2oeblZBXCJVOH4W7ZbA== dependencies: - "@react-native-community/cli-platform-android" "13.6.0" - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-platform-android" "13.6.9" + "@react-native-community/cli-tools" "13.6.9" chalk "^4.1.2" hermes-profile-transformer "^0.0.6" -"@react-native-community/cli-platform-android@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.0.tgz#6f72a2f5e4fc1fc8edb39878179ab9091878b2f9" - integrity sha512-v0kkWU9ezm2n/tZe7lavck3aMaLL1D3YrVcIhgcYiIsZvHVgD48JOKDbqN+23yjoJP6pxVVnBA2AEwz1xLcpAQ== +"@react-native-community/cli-platform-android@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.9.tgz#b175b9b11334fc90da3f395432678bd53c30fae4" + integrity sha512-9KsYGdr08QhdvT3Ht7e8phQB3gDX9Fs427NJe0xnoBh+PDPTI2BD5ks5ttsH8CzEw8/P6H8tJCHq6hf2nxd9cw== dependencies: - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-tools" "13.6.9" chalk "^4.1.2" execa "^5.0.0" fast-glob "^3.3.2" fast-xml-parser "^4.2.4" logkitty "^0.7.1" -"@react-native-community/cli-platform-apple@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.0.tgz#71339336bdecb86b5d7b8503dab6c1e26f6e5aa5" - integrity sha512-VJM5iw9mSxLB6TKhjFf9axORASrSAbiChlZXGMZJD4MGEKrGQE67T16ztH6JxdAug9xcbDFrPekDwzuUXHslMw== +"@react-native-community/cli-platform-apple@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.9.tgz#02fb5dc47d62acd85f4d7a852e93216927a772fa" + integrity sha512-KoeIHfhxMhKXZPXmhQdl6EE+jGKWwoO9jUVWgBvibpVmsNjo7woaG/tfJMEWfWF3najX1EkQAoJWpCDBMYWtlA== dependencies: - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-tools" "13.6.9" chalk "^4.1.2" execa "^5.0.0" fast-glob "^3.3.2" fast-xml-parser "^4.0.12" ora "^5.4.1" -"@react-native-community/cli-platform-ios@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.0.tgz#99ccb4fb0464f63f6de63cbfab5ed61427ab9b50" - integrity sha512-0AcUr1WEmO79FsI4IVCO5izXf16uqY9naDezUAESMD4+UKdn/9Rrf6quBz6M4oowgI/zIagFS6JksFusP8pLFw== +"@react-native-community/cli-platform-ios@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.9.tgz#f37ceab41c2302e8f0d4bcbd3bf58b3353db4306" + integrity sha512-CiUcHlGs8vE0CAB4oi1f+dzniqfGuhWPNrDvae2nm8dewlahTBwIcK5CawyGezjcJoeQhjBflh9vloska+nlnw== dependencies: - "@react-native-community/cli-platform-apple" "13.6.0" + "@react-native-community/cli-platform-apple" "13.6.9" -"@react-native-community/cli-server-api@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-13.6.0.tgz#20d7ec18edf3f945de25b0b7d9ab1c33298269af" - integrity sha512-3FU18/qLo2Mw1aYuIiLOaGiiPOLBeHJ+JZFRyobizvcKHEPHLc+Zt7+iFBPWiro7+pr0tdgV6EEwj1TxypUPpg== +"@react-native-community/cli-server-api@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-13.6.9.tgz#269e666bc26e9d0b2f42c7f6099559b5f9259e9d" + integrity sha512-W8FSlCPWymO+tlQfM3E0JmM8Oei5HZsIk5S0COOl0MRi8h0NmHI4WSTF2GCfbFZkcr2VI/fRsocoN8Au4EZAug== dependencies: - "@react-native-community/cli-debugger-ui" "13.6.0" - "@react-native-community/cli-tools" "13.6.0" + "@react-native-community/cli-debugger-ui" "13.6.9" + "@react-native-community/cli-tools" "13.6.9" compression "^1.7.1" connect "^3.6.5" errorhandler "^1.5.1" nocache "^3.0.1" pretty-format "^26.6.2" serve-static "^1.13.1" - ws "^7.5.1" + ws "^6.2.2" -"@react-native-community/cli-tools@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-13.6.0.tgz#157e18e894bdfb0ff0beca67cc7f80935492a389" - integrity sha512-lnbN3kcwYYT0y70jAfHX+VBjDN5Hb8X7GYy3ergYXrD8eBazthYGhx9wplaOfXGMtmvOSeLu/f13eDTNHPGXxQ== +"@react-native-community/cli-tools@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-13.6.9.tgz#2baee279358ba1a863e737b2fa9f45659ad91929" + integrity sha512-OXaSjoN0mZVw3nrAwcY1PC0uMfyTd9fz7Cy06dh+EJc+h0wikABsVRzV8cIOPrVV+PPEEXE0DBrH20T2puZzgQ== dependencies: appdirsjs "^1.2.4" chalk "^4.1.2" @@ -2509,26 +2509,26 @@ shell-quote "^1.7.3" sudo-prompt "^9.0.0" -"@react-native-community/cli-types@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-13.6.0.tgz#b30053d3409bc876660183e46b8e360bb35ceeba" - integrity sha512-F1M1qKdtMtFzCRvFLAFFTbc1BRHSOMkQA3XLOkpyDb9/DXDeQjkn6gAFczo1xQRJd4aUyvHaEOhSi1RJGEmlMg== +"@react-native-community/cli-types@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-13.6.9.tgz#08bfb796eacf0daeb31e2de516e81e78a36a1a55" + integrity sha512-RLxDppvRxXfs3hxceW/mShi+6o5yS+kFPnPqZTaMKKR5aSg7LwDpLQW4K2D22irEG8e6RKDkZUeH9aL3vO2O0w== dependencies: joi "^17.2.1" -"@react-native-community/cli@13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-13.6.0.tgz#a5bd5ae8543923d539a4b787c1c88ea457f62e93" - integrity sha512-/NhynfYqCPVnxYa6i19+xM5ic8ebcOHQRUQZj9ZiBBZ9A7z34I2JBhSlm06IpUSJ4PgTEViYBMMJjFCRZ+a4wg== - dependencies: - "@react-native-community/cli-clean" "13.6.0" - "@react-native-community/cli-config" "13.6.0" - "@react-native-community/cli-debugger-ui" "13.6.0" - "@react-native-community/cli-doctor" "13.6.0" - "@react-native-community/cli-hermes" "13.6.0" - "@react-native-community/cli-server-api" "13.6.0" - "@react-native-community/cli-tools" "13.6.0" - "@react-native-community/cli-types" "13.6.0" +"@react-native-community/cli@13.6.9": + version "13.6.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-13.6.9.tgz#ba6360b94e0aba9c4001bda256cf7e57e2ecb02c" + integrity sha512-hFJL4cgLPxncJJd/epQ4dHnMg5Jy/7Q56jFvA3MHViuKpzzfTCJCB+pGY54maZbtym53UJON9WTGpM3S81UfjQ== + dependencies: + "@react-native-community/cli-clean" "13.6.9" + "@react-native-community/cli-config" "13.6.9" + "@react-native-community/cli-debugger-ui" "13.6.9" + "@react-native-community/cli-doctor" "13.6.9" + "@react-native-community/cli-hermes" "13.6.9" + "@react-native-community/cli-server-api" "13.6.9" + "@react-native-community/cli-tools" "13.6.9" + "@react-native-community/cli-types" "13.6.9" chalk "^4.1.2" commander "^9.4.1" deepmerge "^4.3.0" @@ -2781,10 +2781,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react@^18.0.18": - version "18.0.20" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.20.tgz#e4c36be3a55eb5b456ecf501bd4a00fd4fd0c9ab" - integrity sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA== +"@types/react@^18.2.6": + version "18.2.67" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.67.tgz#96b7af0b5e79c756f4bdd981de2ca28472c858e5" + integrity sha512-vkIE2vTIMHQ/xL0rgmuoECBCkZFZeHr49HeWSc24AptMbNRo7pwSBvj73rlJJs9fGKj0koS+V7kQB1jHS0uCgw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2876,16 +2876,16 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/eslint-plugin@^6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz#057338df21b6062c2f2fc5999fbea8af9973ac6d" - integrity sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA== +"@typescript-eslint/eslint-plugin@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz#dd71fc5c7ecec745ca26ece506d84d203a205c0e" + integrity sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.7.4" - "@typescript-eslint/type-utils" "6.7.4" - "@typescript-eslint/utils" "6.7.4" - "@typescript-eslint/visitor-keys" "6.7.4" + "@typescript-eslint/scope-manager" "7.1.1" + "@typescript-eslint/type-utils" "7.1.1" + "@typescript-eslint/utils" "7.1.1" + "@typescript-eslint/visitor-keys" "7.1.1" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -2903,15 +2903,15 @@ "@typescript-eslint/typescript-estree" "5.59.5" debug "^4.3.4" -"@typescript-eslint/parser@^6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.4.tgz#23d1dd4fe5d295c7fa2ab651f5406cd9ad0bd435" - integrity sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA== +"@typescript-eslint/parser@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.1.1.tgz#6a9d0a5c9ccdf5dbd3cb8c949728c64e24e07d1f" + integrity sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ== dependencies: - "@typescript-eslint/scope-manager" "6.7.4" - "@typescript-eslint/types" "6.7.4" - "@typescript-eslint/typescript-estree" "6.7.4" - "@typescript-eslint/visitor-keys" "6.7.4" + "@typescript-eslint/scope-manager" "7.1.1" + "@typescript-eslint/types" "7.1.1" + "@typescript-eslint/typescript-estree" "7.1.1" + "@typescript-eslint/visitor-keys" "7.1.1" debug "^4.3.4" "@typescript-eslint/scope-manager@5.59.5": @@ -2922,13 +2922,13 @@ "@typescript-eslint/types" "5.59.5" "@typescript-eslint/visitor-keys" "5.59.5" -"@typescript-eslint/scope-manager@6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz#a484a17aa219e96044db40813429eb7214d7b386" - integrity sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A== +"@typescript-eslint/scope-manager@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz#9e301803ff8e21a74f50c6f89a4baccad9a48f93" + integrity sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA== dependencies: - "@typescript-eslint/types" "6.7.4" - "@typescript-eslint/visitor-keys" "6.7.4" + "@typescript-eslint/types" "7.1.1" + "@typescript-eslint/visitor-keys" "7.1.1" "@typescript-eslint/type-utils@5.59.5": version "5.59.5" @@ -2940,13 +2940,13 @@ debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/type-utils@6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz#847cd3b59baf948984499be3e0a12ff07373e321" - integrity sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ== +"@typescript-eslint/type-utils@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz#aee820d5bedd39b83c18585a526cc520ddb7a226" + integrity sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g== dependencies: - "@typescript-eslint/typescript-estree" "6.7.4" - "@typescript-eslint/utils" "6.7.4" + "@typescript-eslint/typescript-estree" "7.1.1" + "@typescript-eslint/utils" "7.1.1" debug "^4.3.4" ts-api-utils "^1.0.1" @@ -2960,10 +2960,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.4.tgz#5d358484d2be986980c039de68e9f1eb62ea7897" - integrity sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA== +"@typescript-eslint/types@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.1.1.tgz#ca33ba7cf58224fb46a84fea62593c2c53cd795f" + integrity sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q== "@typescript-eslint/typescript-estree@5.59.5": version "5.59.5" @@ -2978,16 +2978,17 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz#f2baece09f7bb1df9296e32638b2e1130014ef1a" - integrity sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ== +"@typescript-eslint/typescript-estree@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz#09c54af0151a1b05d0875c0fc7fe2ec7a2476ece" + integrity sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw== dependencies: - "@typescript-eslint/types" "6.7.4" - "@typescript-eslint/visitor-keys" "6.7.4" + "@typescript-eslint/types" "7.1.1" + "@typescript-eslint/visitor-keys" "7.1.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" + minimatch "9.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" @@ -3005,17 +3006,17 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.4.tgz#2236f72b10e38277ee05ef06142522e1de470ff2" - integrity sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA== +"@typescript-eslint/utils@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.1.1.tgz#bdeeb789eee4af5d3fb5400a69566d4dbf97ff3b" + integrity sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.7.4" - "@typescript-eslint/types" "6.7.4" - "@typescript-eslint/typescript-estree" "6.7.4" + "@typescript-eslint/scope-manager" "7.1.1" + "@typescript-eslint/types" "7.1.1" + "@typescript-eslint/typescript-estree" "7.1.1" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.59.5": @@ -3026,12 +3027,12 @@ "@typescript-eslint/types" "5.59.5" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz#80dfecf820fc67574012375859085f91a4dff043" - integrity sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA== +"@typescript-eslint/visitor-keys@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz#e6538a58c9b157f03bcbb29e3b6a92fe39a6ab0d" + integrity sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ== dependencies: - "@typescript-eslint/types" "6.7.4" + "@typescript-eslint/types" "7.1.1" eslint-visitor-keys "^3.4.1" "@typescript-eslint/visitor-keys@^5.42.0": @@ -4605,10 +4606,10 @@ eslint-plugin-ft-flow@^2.0.1: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-jest@^26.5.3: - version "26.5.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.5.3.tgz#a3ceeaf4a757878342b8b00eca92379b246e5505" - integrity sha512-sICclUqJQnR1bFRZGLN2jnSVsYOsmPYYnroGCIMVSvTS3y8XR3yjzy1EcTQmk6typ5pRgyIWzbjqxK6cZHEZuQ== +eslint-plugin-jest@^27.9.0: + version "27.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b" + integrity sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -7303,6 +7304,13 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"