Compare commits

..
Author SHA1 Message Date
CodemodService Bot b108ad7f5f Fix CQS signal modernize-use-designated-initializers in xplat/js/react-native-github/packages [B] [A] [A]
Reviewed By: cipolleschi

Differential Revision: D80713611
2025-08-21 09:05:04 -07:00
741 changed files with 6594 additions and 14944 deletions
+3 -1
View File
@@ -75,6 +75,8 @@ module.system.haste.module_ref_prefix=m#
react.runtime=automatic
experimental.error_code_migration=new
ban_spread_key_props=true
[lints]
@@ -98,4 +100,4 @@ untyped-import
untyped-type-import
[version]
^0.281.0
^0.279.0
@@ -188,7 +188,6 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
status: 201,
json: () =>
Promise.resolve({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
}),
@@ -209,11 +208,9 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
body: fetchBody,
},
);
expect(response).toEqual({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
});
expect(response).toEqual(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
});
it('creates a draft release for prerelease on GitHub', async () => {
@@ -241,7 +238,6 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
status: 201,
json: () =>
Promise.resolve({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
}),
@@ -262,11 +258,9 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
body: fetchBody,
},
);
expect(response).toEqual({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
});
expect(response).toEqual(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
});
it('throws if the post failes', async () => {
@@ -101,11 +101,7 @@ async function _createDraftReleaseOnGitHub(version, body, latest, token) {
}
const data = await response.json();
const {html_url, id} = data;
return {
html_url,
id,
};
return data.html_url;
}
function moveToChangelogBranch(version) {
@@ -128,8 +124,7 @@ async function createDraftRelease(version, latest, token) {
latest,
token,
);
log(`Created draft release: ${release.html_url}, ID ${release.id}`);
return release;
log(`Created draft release: ${release}`);
}
module.exports = {
+1 -16
View File
@@ -21,24 +21,9 @@ jobs:
git config --local user.name "React Native Bot"
- name: Create draft release
uses: actions/github-script@v6
id: create-draft-release
with:
script: |
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
const version = '${{ github.ref_name }}';
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}')).id;
result-encoding: string
- name: Upload release assets for DotSlash
uses: actions/github-script@v6
env:
RELEASE_ID: ${{ steps.create-draft-release.outputs.result }}
with:
script: |
const {uploadReleaseAssetsForDotSlashFiles} = require('./scripts/releases/upload-release-assets-for-dotslash.js');
const version = '${{ github.ref_name }}';
await uploadReleaseAssetsForDotSlashFiles({
version,
token: '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}',
releaseId: process.env.RELEASE_ID,
});
await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}');
-1
View File
@@ -19,7 +19,6 @@ on:
jobs:
create_release:
if: github.repository == 'facebook/react-native'
runs-on: ubuntu-latest
steps:
- name: Checkout
+3 -6
View File
@@ -23,7 +23,6 @@ jobs:
prepare_hermes_workspace:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_VERSION_FILE: packages/react-native/sdks/.hermesversion
@@ -51,8 +50,8 @@ jobs:
- name: Build HermesC Apple
uses: ./.github/actions/build-hermesc-apple
with:
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
hermes-version: ${{ needs.prepare_hermes_workspace.output.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.output.react-native-version }}
build_apple_slices_hermes:
runs-on: macos-14
@@ -76,7 +75,7 @@ jobs:
uses: ./.github/actions/build-apple-slices-hermes
with:
flavor: ${{ matrix.flavor }}
slice: ${{ matrix.slice }}
slice: ${{ matrix.slice}}
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
@@ -102,7 +101,6 @@ jobs:
flavor: ${{ matrix.flavor }}
prebuild_apple_dependencies:
if: github.repository == 'facebook/react-native'
uses: ./.github/workflows/prebuild-ios-dependencies.yml
secrets: inherit
@@ -147,7 +145,6 @@ jobs:
build_android:
runs-on: 8-core-ubuntu
if: github.repository == 'facebook/react-native'
needs: [set_release_type]
container:
image: reactnativecommunity/react-native-android:latest
@@ -179,9 +179,8 @@ jobs:
- name: Compress and Rename dSYM
if: steps.restore-xcframework.outputs.cache-hit != 'true'
run: |
cd packages/react-native/third-party/Symbols/
tar -cz -f ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz .
mv ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz ./ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
tar -cz -f packages/react-native/third-party/Symbols/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz \
packages/react-native/third-party/Symbols/ReactNativeDependencies.framework.dSYM
- name: Upload XCFramework Artifact
uses: actions/upload-artifact@v4
with:
@@ -9,7 +9,6 @@ on:
jobs:
publish_bumped_packages:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
env:
GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }}
steps:
-2
View File
@@ -21,7 +21,6 @@ jobs:
prepare_hermes_workspace:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_VERSION_FILE: packages/react-native/sdks/.hermesversion
@@ -99,7 +98,6 @@ jobs:
flavor: ${{ matrix.flavor }}
prebuild_apple_dependencies:
if: github.repository == 'facebook/react-native'
uses: ./.github/workflows/prebuild-ios-dependencies.yml
secrets: inherit
-1
View File
@@ -9,7 +9,6 @@ on:
jobs:
rerun:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- name: rerun ${{ inputs.run_id }}
env:
+2 -9
View File
@@ -11,7 +11,6 @@ on:
jobs:
set_release_type:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
outputs:
RELEASE_TYPE: ${{ steps.set_release_type.outputs.RELEASE_TYPE }}
env:
@@ -35,7 +34,6 @@ jobs:
prepare_hermes_workspace:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_VERSION_FILE: packages/react-native/sdks/.hermesversion
@@ -114,7 +112,6 @@ jobs:
flavor: ${{ matrix.flavor }}
prebuild_apple_dependencies:
if: github.repository == 'facebook/react-native'
uses: ./.github/workflows/prebuild-ios-dependencies.yml
secrets: inherit
@@ -276,8 +273,7 @@ jobs:
NEW_ARCH_ENABLED=1
export RCT_USE_LOCAL_RN_DEP=/tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
# Disable prebuilds for now, as they are causing issues with E2E tests for 0.82-stable branch
# export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
xcodebuild \
@@ -383,8 +379,7 @@ jobs:
runs-on: 8-core-ubuntu
needs: [set_release_type]
container:
# Version is pinned to v18.0 to unblock `run_fantom_tests` - see https://github.com/react-native-community/docker-android/pull/242#issuecomment-3280029122
image: reactnativecommunity/react-native-android:v18.0
image: reactnativecommunity/react-native-android:latest
env:
TERM: "dumb"
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
@@ -599,7 +594,6 @@ jobs:
test_js:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
strategy:
fail-fast: false
matrix:
@@ -614,7 +608,6 @@ jobs:
lint:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
@@ -1,49 +0,0 @@
name: Validate DotSlash Artifacts
on:
workflow_dispatch:
release:
types: [published]
push:
branches:
- main
paths:
- packages/debugger-shell/bin/react-native-devtools
- "scripts/releases/**"
- package.json
- yarn.lock
pull_request:
branches:
- main
paths:
- packages/debugger-shell/bin/react-native-devtools
- "scripts/releases/**"
- package.json
- yarn.lock
# Same time as the nightly build: 2:15 AM UTC
schedule:
- cron: "15 2 * * *"
jobs:
validate-dotslash-artifacts:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Configure Git
shell: bash
run: |
git config --local user.email "bot@reactnative.dev"
git config --local user.name "React Native Bot"
- name: Validate DotSlash artifacts
uses: actions/github-script@v6
with:
script: |
const {validateDotSlashArtifacts} = require('./scripts/releases/validate-dotslash-artifacts.js');
await validateDotSlashArtifacts();
-266
View File
@@ -1,271 +1,5 @@
# Changelog
## v0.81.3
### Fixed
#### iOS specific
- Reverted "Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source" ([537e3ad930](https://github.com/facebook/react-native/commit/537e3ad93041c0cef959f0d20586fe97818900a6) by [@gabrieldonadel](https://github.com/gabrieldonadel))
## v0.81.2
### Added
#### Android specific
- Create a debugOptimized buildType for Android ([5e3edafec6](https://github.com/facebook/react-native/commit/5e3edafec6c69558521061dced7a6bcd046576b0) by [@cortinico](https://github.com/cortinico))
- Add `useNativeEqualsInNativeReadableArrayAndroid` and `useNativeTransformHelperAndroid` feature flag to the experimental channel. This should alleviate some of the perf issue users are seeing on Android + Reanimated + NewArch on 0.81([a346096da8](https://github.com/facebook/react-native/commit/a346096da81fccf5fbc82d83bfc128695e6ec3a5) by [@cortinico](https://github.com/cortinico))
### Fixed
#### iOS specific
- Fix Node scripts related to prebuilt tarball extraction for paths containing whitespaces ([366f2ad505](https://github.com/facebook/react-native/commit/366f2ad5057ffecc1f5b211f6aae29567ae6b7e5) by [@kitten](https://github.com/kitten))
- Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source ([a2eb29e5e7](https://github.com/facebook/react-native/commit/a2eb29e5e7aef8bbdf4c647c8467b5292b013b20) by [@kitten](https://github.com/kitten))
## v0.82.0-rc.1
### Added
#### Android specific
- **Hermes V1:** Added opt-in to use the new Hermes ([3e9990f860](https://github.com/facebook/react-native/commit/3e9990f860eb9380837ef431ca02def32c4261ad) by [@j-piasecki](https://github.com/j-piasecki))
#### iOS specific
- **Hermes V1:** Added opt-in to use the new Hermes ([e9cdc308b4](https://github.com/facebook/react-native/commit/e9cdc308b4c04753d85757e8877ac00c3c687b95) by [@j-piasecki](https://github.com/j-piasecki))
### Changed
- **Hermes V1:** Changed the source of hermesc binary to be an npm package ([2e0bd13a25](https://github.com/facebook/react-native/commit/2e0bd13a2533fe7ab64125a95b9215b806018c6e) by [@j-piasecki](https://github.com/j-piasecki))
### Deprecated
- **APIs:** Deprecate legacy javascript react native apis ([e7aeea26bd](https://github.com/facebook/react-native/commit/e7aeea26bde6e9cda0a3a0a55fc2a0421fb0c0e5) by [@RSNara](https://github.com/RSNara))
### Fixed
#### Android specific
- **Build From Source:** Fix build from source due to missing folder error on Gradle 9.0 ([9fbce3eff1](https://github.com/facebook/react-native/commit/9fbce3eff18060f16e796badc415ba733ede19af) by [@cortinico](https://github.com/cortinico))
#### iOS specific
- **RCTAlertController:** Simplify RCTAlertController, don't create additional UIWindow ([05c4321b19](https://github.com/facebook/react-native/commit/05c4321b194c3d0e146b6085bcaccc75acd3fd67) by [@okwasniewski](https://github.com/okwasniewski))
- **Prebuild:** Fix Node scripts related to prebuilt tarball extraction for paths containing whitespaces ([9731e8ebc5](https://github.com/facebook/react-native/commit/9731e8ebc5ea87526a91b9903172639e062cd920) by [@kitten](https://github.com/kitten))
- **Prebuild:** Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source ([f170db412b](https://github.com/facebook/react-native/commit/f170db412b3ab46fd0894d5d66431d9c230cd3a8) by [@kitten](https://github.com/kitten))
## v0.82.0-rc.0
### Breaking
- **Appearance.setColorScheme:** `Appearance.setColorScheme` no longer accepts a nullable value ([a4581ecd8b](https://github.com/facebook/react-native/commit/a4581ecd8b6df5efa44dfe6d43708320209c900b) by [@huntie](https://github.com/huntie))
- **`CxxSharedModuleWrapper`:** Removed CxxSharedModuleWrapper ([fafbee2402](https://github.com/facebook/react-native/commit/fafbee240235ea0e63eb01abd31ce32d6a576429) by [@javache](https://github.com/javache))
- **DOM API:** Enable DOM APIs in host component refs ([2ad845ccb2](https://github.com/facebook/react-native/commit/2ad845ccb2fea277e05513dcf41407026a8224f0) by [@rubennorte](https://github.com/rubennorte))
- **Error Handling:** Unhandled promises are now handled by ExceptionsManager.handleException, instead of being swallowed as Logbox Warnings. ([c4082c9ce2](https://github.com/facebook/react-native/commit/c4082c9ce208a324c2d011823ca2ba432411aafc) by [@krystofwoldrich](https://github.com/krystofwoldrich))
- **`shouldEmitW3CPointerEvents`:** Migrate `shouldPressibilityUseW3CPointerEventsForHover` to common private feature flags and remove `shouldEmitW3CPointerEvents` flag. ([fb4587780e](https://github.com/facebook/react-native/commit/fb4587780e8d6111139d73598a9a26ff392dee28) by [@coado](https://github.com/coado))
- **TurboModuleUtils:** Remove unused ReactCommon/TurboModuleUtils functions #deepCopyJSIObject and #deepCopyJSIArray ([ead669ade3](https://github.com/facebook/react-native/commit/ead669ade31ee703c407f96c0ce98d8f2991bdc8) by [@christophpurrer](https://github.com/christophpurrer))
#### Android specific
- **Deps:** Gradle to 9.0 ([7f93b664b4](https://github.com/facebook/react-native/commit/7f93b664b41ba11226aae7cca0e7c9b7f38a7d18) by [@cortinico](https://github.com/cortinico))
- **Image Prefetching:** Android: Image Prefetching send ImageResizeMode as enum value ([e30f34eda6](https://github.com/facebook/react-native/commit/e30f34eda689994cab8cd62aa38175238da8638b) by [@christophpurrer](https://github.com/christophpurrer))
- **New Architecture:** Remove possibility to newArchEnabled=false in 0.82 ([d5d21d0614](https://github.com/facebook/react-native/commit/d5d21d061493ee973c789a7c6ab8cceebc1f04f9) by [@cortinico](https://github.com/cortinico))
- **`reactNativeHost`:** Throw Exception if ReactApplication.reactNativeHost is not overriden ([0d3791ca0a](https://github.com/facebook/react-native/commit/0d3791ca0ab30d5a12881c9901f31291b3e998c6) by [@mdvacca](https://github.com/mdvacca))
- **ViewManagerInterfaces:** Migrate ViewManagerInterfaces to kotlin. Some types in code generated ViewManagerInterfaces might differ. e.g. this will start enforcing nullability in parameters of viewManagerInterface methods (e.g. String commands parameters are not nullable, view params are not nullable in any method, etc) ([79ca9036d3](https://github.com/facebook/react-native/commit/79ca9036d39c16cd115dc0427cb7092f358ac47e) by [@mdvacca](https://github.com/mdvacca))
#### iOS Specific
- **New Architecture:** Removed the opt-out from the New Architecture. ([83e6eaf693](https://github.com/facebook/react-native/commit/83e6eaf693f967b7870a5d4896cbb799206a14f0) by [@cipolleschi](https://github.com/cipolleschi))
### Added
- **Animated:** `Animated.CompositeAnomation` is now exposed when using `"react-native-strict-api"` ([024d25794a](https://github.com/facebook/react-native/commit/024d25794a51c94c877c1dfa115a82ebbf559614) by [@huntie](https://github.com/huntie))
- **Animated:** Allow calling createAnimatedNode without batching ([d9d9a49e18](https://github.com/facebook/react-native/commit/d9d9a49e18f3c51caa18cf7da0a1fcd62f1ecf18) by [@zeyap](https://github.com/zeyap))
- **Animated:** Allow filter usage with native animated driver. ([138d0eb01d](https://github.com/facebook/react-native/commit/138d0eb01dbe597261459a37d364d1780c3ef228) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
- **API:** Expose NativeComponentRegistry API as JavaScript root export ([f936780cd5](https://github.com/facebook/react-native/commit/f936780cd5c0c17797f9d2bbc8f5cee81c2eefce) by [@zhongwuzw](https://github.com/zhongwuzw))
- **API:** Expose `ReactNativeVersion` API as JavaScript root export ([ec5638abd0](https://github.com/facebook/react-native/commit/ec5638abd0e872be62b6ea5d8df9bed6335c2191) by [@huntie](https://github.com/huntie))
- **Codegen:** Added getDebugProps to codegen ([e547f466ee](https://github.com/facebook/react-native/commit/e547f466ee41415a75ec6b6f910171285ee7bfc3) by [@cipolleschi](https://github.com/cipolleschi))
- **Pressable:** Allow setting `blockNativeResponder` on Pressable ([6e4d23ded2](https://github.com/facebook/react-native/commit/6e4d23ded2da4a717bafcc032e3d7a0a5fbe3731) by [@zeyap](https://github.com/zeyap))
- **Yoga/API:** Make yoga/Yoga.h an umbrell header ([8ed2cee80e](https://github.com/facebook/react-native/commit/8ed2cee80e0aaac2f2a6a897ba450888f274a5a4) by [@rudybear](https://github.com/rudybear))
#### Android specific
- **Build Type:** Create a `debugOptimized` `buildType` for Android ([eb2461c7c9](https://github.com/facebook/react-native/commit/eb2461c7c902ebed272bd2d22d6cff4d3c586da6) by [@cortinico](https://github.com/cortinico))
- **DevMenu:** Add long-press back as an option to open the DevMenu for devices that lack menu & fast-forward. ([32d37f03ad](https://github.com/facebook/react-native/commit/32d37f03ad05290205a4f04d756f6e1880c4ff89) by [@sbuggay](https://github.com/sbuggay))
- **DevTools:** `DevSupportManager::openDebugger` now supports an optional `panel` param determining the starting panel ([7eb3536728](https://github.com/facebook/react-native/commit/7eb3536728c4a20f7e51245f4f7b64aa505bd799) by [@huntie](https://github.com/huntie))
- **DevTools:** Adds a landing view parameter to opening RNDT, enabling arbitrary view focus on launch. ([635c707eec](https://github.com/facebook/react-native/commit/635c707eec18f6d2ceceac2dcee9f458f17f8aab) by [@sbuggay](https://github.com/sbuggay))
- **HWInput:** Channel up/down hardware events. ([c2a3e4420e](https://github.com/facebook/react-native/commit/c2a3e4420e07147f9a040a665da98dbe22b87a2a) by [@sbuggay](https://github.com/sbuggay))
- **Manifest:** Add support to specify a single Manifest rather than 2 (main/debug) by using the `usesCleartextTraffic` manifest placeholder which is autoconfigured by RNGP. ([d89acc1596](https://github.com/facebook/react-native/commit/d89acc1596345534882938d2bbf40275a6cb89bd) by [@cortinico](https://github.com/cortinico))
#### iOS specific
- **API:** Add deprecation message for RCTAppdelegate APIs ([d503ea4efc](https://github.com/facebook/react-native/commit/d503ea4efc84b6511cef2a46421a16e044862e88) by [@cipolleschi](https://github.com/cipolleschi))
- **New Architecture:** Add warning if RCT_NEW_ARCH_ENABLED is set to 0 ([7d0bef2f25](https://github.com/facebook/react-native/commit/7d0bef2f25a206d917e7f5cc2b9a6c088f13a832) by [@cipolleschi](https://github.com/cipolleschi))
### Changed
- **Font:** Enabled `enableFontScaleChangesUpdatingLayout` feature flag by default ([686d14f1d1](https://github.com/facebook/react-native/commit/686d14f1d16c2f02720104ddd395f7d27c908350) by [@j-piasecki](https://github.com/j-piasecki))
- **Hermes:** Changed names of hermes binaries ([776fca1e7c](https://github.com/facebook/react-native/commit/776fca1e7c978a2d8f817d042836073e4dcb4e0e) by [@j-piasecki](https://github.com/j-piasecki))
- **Metro:** Bump Metro to ^0.83.1 ([840fd6c83f](https://github.com/facebook/react-native/commit/840fd6c83f45326a796bf2823f8c2fa942aed06c) by [@robhogan](https://github.com/robhogan))
- **React:** Bumped React to 19.1.1 ([ec5a98b1f5](https://github.com/facebook/react-native/commit/ec5a98b1f5c2137f5f6ff5f5f6706f20384c44df) by [@cipolleschi](https://github.com/cipolleschi))
- **Runtime:** CDP backend now accepts `addBinding` and `removeBinding` methods earlier, before a Runtime exists. ([3271e57c75](https://github.com/facebook/react-native/commit/3271e57c751e7d1193c1e9f7b53e545231511b9d) by [@motiz88](https://github.com/motiz88))
- **Typing:** Update types for Platform.version ([f6ba2dbf3b](https://github.com/facebook/react-native/commit/f6ba2dbf3b4c85da1a7f9079fd366a41b160fa69) by [@riteshshukla04](https://github.com/riteshshukla04))
- **UIManager:** Avoid unnecessary copy of view props map in UIManager::updateShadowTree ([5b38bb4745](https://github.com/facebook/react-native/commit/5b38bb47457f853c2c3d5f275facbb9fbc150683) by [@zeyap](https://github.com/zeyap))
#### Android specific
- **AGP:** AGP to 8.12.0 ([742ef3d661](https://github.com/facebook/react-native/commit/742ef3d6615c8c1202e9f683e6127ac97d7a9e23) by [@cortinico](https://github.com/cortinico))
- **DevSupportManager:** DevSupport `openDebugger()` methods now accept a `panel: String?` param. Frameworks directly implementing `DevSupportManager` will need to adjust call signatures. ([9dba7112cf](https://github.com/facebook/react-native/commit/9dba7112cfd09b02300869a77dba3dca16f49a28) by [@huntie](https://github.com/huntie))
- **Kotlin:**Migrated TextAttributeProps to Kotlin. You might need to update your property access to use camelCase instead of Hungarian notation. ([fa921b3c7b](https://github.com/facebook/react-native/commit/fa921b3c7b289800a79196468f993a0eb0bf693f) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrated ReactBaseTextShadowNode to Kotlin. You might need to update your property access to use camelCase instead of Hungarian notation. ([8ccfff9a46](https://github.com/facebook/react-native/commit/8ccfff9a46f317fd78f478c8b3f180441535d1ca) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrated com.facebook.react.bridge.Arguments to Kotlin. ([2534aeaddb](https://github.com/facebook/react-native/commit/2534aeaddb0490b69dfaba6b8d316616c7e10a9c) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaConfig` to Kotlin ([4d5caef76b](https://github.com/facebook/react-native/commit/4d5caef76b83eb7e983364ecc81abb6027e5f98e) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaValue` to Kotlin ([4340dcbae8](https://github.com/facebook/react-native/commit/4340dcbae8fc41cde844e805a1ebfc23d23d164f) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaNative` to Kotlin ([bc54a06fcb](https://github.com/facebook/react-native/commit/bc54a06fcb5b5d1efd8996d8568733b657fc1b06) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaConfigFactory` to Kotlin ([33ca53d9db](https://github.com/facebook/react-native/commit/33ca53d9dbe53b92d65f82dbd53a2e9f23efd4f3) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `DoNotStrip` to Kotlin ([35d8086881](https://github.com/facebook/react-native/commit/35d8086881fac643b0ebc0d53aaf7e79b7ccd830) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaLayoutType` to Kotlin ([7e461003c6](https://github.com/facebook/react-native/commit/7e461003c6592c8c539960bd5e8169c48dd27f50) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `LayoutPassReason` to Kotlin ([db2a9c089c](https://github.com/facebook/react-native/commit/db2a9c089cd5802d99e0fc86e4dc0dbf7c888307) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaNodeFactory` to Kotlin ([40afa75a7c](https://github.com/facebook/react-native/commit/40afa75a7c816a5581223c7bcd1b65b8713edf47) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaMeasureOutput` to Kotlin ([453508ada8](https://github.com/facebook/react-native/commit/453508ada837554455733e3ca94440a7143f51b1) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaMeasureFunction` to Kotlin ([05eddd354e](https://github.com/facebook/react-native/commit/05eddd354e2e80ad3c95ed5a2199a59a77317891) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaStyleInputs` to Kotlin ([001736000f](https://github.com/facebook/react-native/commit/001736000f69ce98db86c17707408bbf3f0ae9a5) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaBaselineFunction` to Kotlin ([a2eb3b299d](https://github.com/facebook/react-native/commit/a2eb3b299dddea60c510821b662dfed55b334df7) by [@mateoguzmana](https://github.com/mateoguzmana))
- **Kotlin:** Migrate `YogaLogger` to Kotlin ([9c9a39b58e](https://github.com/facebook/react-native/commit/9c9a39b58e12bc734c27a5d9306e792b0dcaf927) by [@mateoguzmana](https://github.com/mateoguzmana))
- **OnBatchCompleteListener:** Make OnBatchCompleteListener interface internal ([046ff8e58b](https://github.com/facebook/react-native/commit/046ff8e58bed5da0f19adc860b327c7248b19f48) by [@cortinico](https://github.com/cortinico))
- **ReactSurface:** Changed return type of ReactSurfaceImpl.view to ReactSurfaceView to align with parameter recived by ReactSurfaceImpl.attachView() ([41029d8e91](https://github.com/facebook/react-native/commit/41029d8e91492c34c377374b442b31755874618c) by [@mdvacca](https://github.com/mdvacca))
- **TextAttributeProps:** Deprecate the field `TextAttributeProps.effectiveLineHeight`. This field was public but never used in OSS. ([ede037ade7](https://github.com/facebook/react-native/commit/ede037ade795bd44725f9bd82cace193a74aa68d) by [@cortinico](https://github.com/cortinico))
- **ViewManagers:** Changed method arguments names for Core ViewManagers to match the names of ViewManagerInterfaces ([e7d9e0d197](https://github.com/facebook/react-native/commit/e7d9e0d1977c136a85b9a78ef36a258631d1e9ba) by [@mdvacca](https://github.com/mdvacca))
### Deprecated
- **StyleSheet:** `StyleSheet.absoluteFillObject` is deprecated in favor of `StyleSheet.absoluteFill` (equivalent). ([83e19813ff](https://github.com/facebook/react-native/commit/83e19813ff5498ab3497d97fe38dba63a5554425) by [@huntie](https://github.com/huntie))
- Deprecate all the c++ classes not used by interop, or the new architecture. ([9539cd2626](https://github.com/facebook/react-native/commit/9539cd26261aef646379104833c7f719e3d83d02) by [@RSNara](https://github.com/RSNara))
#### Android specific
- **DevMenu:** Remove bridge mode string from React Native Dev Menu title ([1c838f32a9](https://github.com/facebook/react-native/commit/1c838f32a9bcee3867ec0502b344889308302f26) by [@sbuggay](https://github.com/sbuggay))
- **New Architecture:** DefaultDevSupportManagerFactory.create() method used for Old Arch ([026e22bb8d](https://github.com/facebook/react-native/commit/026e22bb8d7b38b3bd66ffcc7d4ee446adfee943) by [@cortinico](https://github.com/cortinico))
- **New Architecture:** Deprecate `BridgelessReactContext.getCatalystInstance()` method ([4583fbe052](https://github.com/facebook/react-native/commit/4583fbe052924df1ad030e51ad80e8d754a4c5a4) by [@cortinico](https://github.com/cortinico))
- **New Architecture:** Deprecate legacy architecture classes ReactInstanceManager and ReactInstanceManagerBuilder, these classes will be deleted in a future release ([fb84932e48](https://github.com/facebook/react-native/commit/fb84932e4894a45c0a2725e1d665acdf7bcea435) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Depreacate `CoreModulesPackage` and `NativeModuleRegistryBuilder` legacy architecture classes, these classes unused in the new architecture and will be deleted in the future ([d3bbbd893a](https://github.com/facebook/react-native/commit/d3bbbd893acd500237ab4e1778c6a2e0fe1948a9) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate Legacy Architecture ViewManagers, these classes are not used as part of the new architecture and will be deleted in the future ([da74d5da2c](https://github.com/facebook/react-native/commit/da74d5da2cac5306e37368c65490c434e7ff9f4f) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture ShadowNode classes included in React Native ([07091a9ae8](https://github.com/facebook/react-native/commit/07091a9ae8d70a601d969d9def4952563d3b7bcf) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Depreacte all LegacyArchitecture classes from the bridge package ([c1f7c5e321](https://github.com/facebook/react-native/commit/c1f7c5e3217a7e8a77a859652aadff2a41e3ea58) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture class UIManagerProvider ([b29b86f275](https://github.com/facebook/react-native/commit/b29b86f27553eac50daa18ffb6bca07be3f24f25) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate BridgeDevSupportManager and JSInstance ([25c011eb4d](https://github.com/facebook/react-native/commit/25c011eb4d403040b57e338bec704769de20793c) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate NativeModuleRegistry Legacy Architecture class ([22e4c25211](https://github.com/facebook/react-native/commit/22e4c252116da1a6658b15a84720e0ee314dddd6) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate subset of LegacyArchitecture classes in com/facebook/react/bridge ([78a3ff81eb](https://github.com/facebook/react-native/commit/78a3ff81eb38ae26fb15106580de841477897101) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture class FrescoBasedReactTextInlineImageShadowNode ([25f466cc4d](https://github.com/facebook/react-native/commit/25f466cc4dd28c962b967475c04c284d26efb722) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate Legacy Architecture class CallbackImpl ([718126fcf0](https://github.com/facebook/react-native/commit/718126fcf0296969ee659c31fae51b4317c896d3) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture class JavaMethodWrapper ([19a99dd088](https://github.com/facebook/react-native/commit/19a99dd0882d786daea3db486fd3aed3c10419b5) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate Legacy Architecture ShadowNode classes ([c4715886a9](https://github.com/facebook/react-native/commit/c4715886a917eb3eb63aab366de5225090dff5a1) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture UIManagerModules class ([85610c8b43](https://github.com/facebook/react-native/commit/85610c8b43ea132154cddcfed973ee6ceb3e55b3) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture classes from com/facebook/react/uimanager ([7f5b2b8f84](https://github.com/facebook/react-native/commit/7f5b2b8f84d7941891a447978c6adc17929ef87f) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture classes from package com.facebook.react.uimanager ([39d24bade3](https://github.com/facebook/react-native/commit/39d24bade317920544a3715e3a1f131663d8cded) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** Deprecate LegacyArchitecture classes from LayoutAnimation package ([f67078df07](https://github.com/facebook/react-native/commit/f67078df07b6c9ad995eb43ff47fc4a43bb2eaee) by [@mdvacca](https://github.com/mdvacca))
- **New Architecture:** ReactPackageLogger is not supported in the new architecture and being deprecated ([65671108f6](https://github.com/facebook/react-native/commit/65671108f69d9b23a011841102e4141293581d9c) by [@mdvacca](https://github.com/mdvacca))
#### iOS specific
- **DevMenu:** Remove bridge mode title and description from React Native Dev Menu title ([775daf5972](https://github.com/facebook/react-native/commit/775daf597280db94354ed484f2ce81690f1eb7b0) by [@sbuggay](https://github.com/sbuggay))
- **New Architecture:** Deprecate all the objc classes not used by interop, or the new architecture. ([70f53ac4ea](https://github.com/facebook/react-native/commit/70f53ac4ea144020560906f5931e480ed4dee87c) by [@RSNara](https://github.com/RSNara))
### Removed
- **New Architecture:** Core: Remove legacy components ([9c8a4c2297](https://github.com/facebook/react-native/commit/9c8a4c22973c7ce6fcf6b5d22c6d5fd4c6dc0d92) by [@RSNara](https://github.com/RSNara))
#### Android specific
- **DefaultReactHost:** Delete unused `DefaultReactHost.getDefaultReactHost()` overload ([d35ddb5e59](https://github.com/facebook/react-native/commit/d35ddb5e59a8cb990dd61a154a8e15e9542f8b15) by [@cortinico](https://github.com/cortinico))
- **DefaultReactHost:** Remove deprecated DefaultReactHost.getDefaultReactHost() overload - part 2 ([bda6acf3b0](https://github.com/facebook/react-native/commit/bda6acf3b08779c0dae7bdadbc9913eea79acd0d) by [@cortinico](https://github.com/cortinico))
- **DefaultReactHost:** Remove deprecated DefaultReactHost.getDefaultReactHost() overload - part 1 ([474f455a75](https://github.com/facebook/react-native/commit/474f455a7591049382da0d0308ddd21589f0cc7e) by [@cortinico](https://github.com/cortinico))
- **Inspector:** Removed unused `Inspector` public class from React Android ([cf528526cc](https://github.com/facebook/react-native/commit/cf528526cc375f1003125cf63f66fbd88790ceae) by [@cortinico](https://github.com/cortinico))
- **JSONArguments:** Remove the `com.facebook.react.bridge.JSONArguments` class ([04ae15d99b](https://github.com/facebook/react-native/commit/04ae15d99bb2ee6f7987bbe8c3d7acfdd46a482f) by [@cortinico](https://github.com/cortinico))
- **MessageQueueThreadPerfStats:** Deprecated MessageQueueThreadPerfStats API and replaced with stub. ([3bf5cb3d0e](https://github.com/facebook/react-native/commit/3bf5cb3d0e7d9d1749ef19a8392b9bbd3ec7ab7d) by [@javache](https://github.com/javache))
### Fixed
- **Accessibility:** Fix for setting the default value for accessibility props ([586f5ba89c](https://github.com/facebook/react-native/commit/586f5ba89cc20a81a9e2d5d0f2708e9cd1b440c0) by Vineeth K)
- **Accessibility:** `aria-hidden` support for `Text`, non-editable `TextInput` and `Image` ([0f39fc3000](https://github.com/facebook/react-native/commit/0f39fc3000411a43711814e0ab9cca1f7093b625) by [@mdjastrzebski](https://github.com/mdjastrzebski))
- **Build:** Fixed babel plugin validation error when coverage instrumentation is enabled ([191ddc1ec7](https://github.com/facebook/react-native/commit/191ddc1ec72be6641ebb8b9cb729cf0e142fff55) by Umar Mohammad)
- **Casting:** Casting rawValue to int was incorrectly truncating ([31b9f10364](https://github.com/facebook/react-native/commit/31b9f103645e67586bdfc5c2f590c28c04ca3871) by [@javache](https://github.com/javache))
- **Codegen:** Help Codegen find library's package.json if some libraries using `exports` field in their package.json file and the `./package.json` subpath is not explicitly defined ([739dfd2141](https://github.com/facebook/react-native/commit/739dfd2141015a8126448bda64a559f5bf22672e) by [@RakaDoank](https://github.com/RakaDoank))
- **Hermes:** Change leftover references to `hermes.framework` to `hermesvm.framework` ([7f051c5470](https://github.com/facebook/react-native/commit/7f051c54701b3585f76f63846abbf7e68e2688d2) by [@j-piasecki](https://github.com/j-piasecki))
- **Performance Panel:** Fix typo in Performance.js type checking condition ([6caf2dfa38](https://github.com/facebook/react-native/commit/6caf2dfa382fd4f1184b8d21b030c36687a256e4) by [@YangJonghun](https://github.com/YangJonghun))
- **Performance Panel:** Add default cases to switch statements in headers ([323fe3a5d4](https://github.com/facebook/react-native/commit/323fe3a5d471ae5a2f94d5c2bd13cc97feffe0a5) by [@NSProgrammer](https://github.com/NSProgrammer))
- **ReactCommon:** Bring back ContextContainer::Shared = std::shared_ptr<const ContextContainer> alias ([daeb6e99ab](https://github.com/facebook/react-native/commit/daeb6e99abbca2b6395a9a703d2b0bb9e5091fb7) by [@christophpurrer](https://github.com/christophpurrer))
- **ReactCommon:** Bring back SharedImageManager = std::shared_ptr<ImageManager> alias ([4718b35259](https://github.com/facebook/react-native/commit/4718b35259135b3503033a0061ae84e15d4eb450) by [@christophpurrer](https://github.com/christophpurrer))
- **ReactCommon:** Fixed Type Conversion Error in DynamicEventPayload ([ff38d59cff](https://github.com/facebook/react-native/commit/ff38d59cff92e0a50f0dd70384fbc4dd11d969c4) by Harini Malothu)
- **ReactCommon:** Fixed Type Conversion Error in CSSHexColor ([2ca88a0069](https://github.com/facebook/react-native/commit/2ca88a0069969bf115da6f0ea9f2fbbae9c9226c) by [@anupriya13](https://github.com/anupriya13))
- **TestCallInvoker:** Fix memory leak in TestCallInvoker ([9f2fbc23e4](https://github.com/facebook/react-native/commit/9f2fbc23e48af9be56b3729d514fbb3fff4ba376) by [@christophpurrer](https://github.com/christophpurrer))
#### Android specific
- **Accessability:** Stabilize custom accessibility action IDs to prevent "incompatible action" errors in TalkBack. ([626568f9a3](https://github.com/facebook/react-native/commit/626568f9a3f956a52f6c55df1dc3bc5cd017e353) by [@leg234-png](https://github.com/leg234-png))
- **Determinism:** Turned off build IDs for native libraries, fixing issues with reproducibility ([4b8dbe7642](https://github.com/facebook/react-native/commit/4b8dbe7642be53d0ccfc68ca8c9b3f5e750a68c0) by [@Rexogamer](https://github.com/Rexogamer))
- **DevTools:** Fix stack trace linkifying failing when using Android emulator and other situations where the device and debugger have different bundle urls ([794df48ad6](https://github.com/facebook/react-native/commit/794df48ad6a259022e66de1a38ff54b5ec67c3e4) by [@vzaidman](https://github.com/vzaidman))
- **Edge to Edge:** Fix `Dimensions` `window` values on Android < 15 when edge-to-edge is enabled ([3b185e4bce](https://github.com/facebook/react-native/commit/3b185e4bcef24e0689cccd4cf250d469b114d4da) by [@zoontek](https://github.com/zoontek))
- **Fonts:** Update font scale when recreating `RootView` ([5cda3065ce](https://github.com/facebook/react-native/commit/5cda3065ce635460a7458cbab5c10e24bea3bfe2) by [@j-piasecki](https://github.com/j-piasecki))
- **Fonts:** Fix incorrect positioning of inline view at the end of string when RTL text in LTR container ([7f224941bb](https://github.com/facebook/react-native/commit/7f224941bb807919b487d8e1634dd2124f9258b8) by [@NickGerleman](https://github.com/NickGerleman))
- **Locale:** Use the first available locale instead of the default one to decide `isDevicePreferredLanguageRTL` ([a03780d279](https://github.com/facebook/react-native/commit/a03780d279d0944e0dcbbf5a93680775006598b0) by Kaining Zhong)
- **New Architecture:** Correctly account for insets on first render of Modals on New Arch ([2e76fc8e8e](https://github.com/facebook/react-native/commit/2e76fc8e8ea01fbce5bd131f675364e688f49088) by [@cortinico](https://github.com/cortinico))
- **Performance:** Fix mounting is very slow on Android by shipping native transform optimizations ([c557311ed8](https://github.com/facebook/react-native/commit/c557311ed836cded8548c5bca32f3eded0abc7ff) by [@cortinico](https://github.com/cortinico))
- **Scroll:** Fixed an issue where shadow tree and native tree layouts mismatch at the end of a scroll event ([1828c53f85](https://github.com/facebook/react-native/commit/1828c53f85faf599a485b3859f8b62586696265f) by [@Abbondanzo](https://github.com/Abbondanzo))
- **Start up:** Fix wrong default for `jsBundleAssetPath` on `DefaultReactHost` ([2246e2b82c](https://github.com/facebook/react-native/commit/2246e2b82cf0c433f9a9b385ea98e532c6f322c6) by [@cortinico](https://github.com/cortinico))
#### iOS specific
- **Build:** Fixed using USE_FRAMEWORKS (static/dynamic) with precompiled binaries ([e723ca4d6b](https://github.com/facebook/react-native/commit/e723ca4d6b86d5a98449498395c700513ceba555) by [@chrfalch](https://github.com/chrfalch))
- **Build:** Non-UTF8 crashes Info.plist local frameworks ([91e69b5d4c](https://github.com/facebook/react-native/commit/91e69b5d4c768278680a8d9ae979bc267624ce98) by [@philipheinser](https://github.com/philipheinser))
- **Build:** Fixed variable naming error in `set_fast_float_config` method in `react_native_pods.rb` ([327057fad5](https://github.com/facebook/react-native/commit/327057fad5c78a95e6c039bfe380d78672e83a43) by [@eliotfallon213](https://github.com/eliotfallon213))
- **Build:** Fix pure cocoapods dynamic framework build ([aa4555eaf1](https://github.com/facebook/react-native/commit/aa4555eaf1b6aab83660c600e867fa6c2da4128e) by [@cipolleschi](https://github.com/cipolleschi))
- **Native Modules:** Fix concurrent calls into resolve/reject inside native modules ([dc879950d1](https://github.com/facebook/react-native/commit/dc879950d196dfd429229f1c4c8e743ef1799d11) by [@RSNara](https://github.com/RSNara))
- **New Architecture:** Fix overriding (xc)framework Info.plist files with RCTNewArchEnabled field ([f84514a88b](https://github.com/facebook/react-native/commit/f84514a88be00f8dcae7972f84aa89d829392a58) by [@msynowski](https://github.com/msynowski))
- **RCTPullToRefreshViewComponentView:** Properly initialize the `RCTPullToRefreshViewComponentView` ([27217e8bd6](https://github.com/facebook/react-native/commit/27217e8bd601757b5db6efc022db428b552a2aa4) by [@cipolleschi](https://github.com/cipolleschi))
- **RCTReactNativeFactory:** Ask the delegate for `getModuleForClass` and `getModuleInstanceFromClass` ([85b47afb48](https://github.com/facebook/react-native/commit/85b47afb48e50b036d2c2c79a008f571d3bfcb43) by [@cipolleschi](https://github.com/cipolleschi))
- **ScrollView:** Correctly propagate `ScrollView` props to `RefreshControl` ([09daad27ea](https://github.com/facebook/react-native/commit/09daad27ea22b83fab65176ea3c7f5f1488ba408) by [@cipolleschi](https://github.com/cipolleschi))
- **ScrollView:** Make sure that `ScrollView` recycled refresh control have the right props setup. ([21b93d8d7d](https://github.com/facebook/react-native/commit/21b93d8d7d46a26f728df19764f85a8aebf318bb) by [@cipolleschi](https://github.com/cipolleschi))
- **Switch:** Fixed a crash when rendering the `Switch` component ([28275a0f7b](https://github.com/facebook/react-native/commit/28275a0f7b182a215010d47fb841d9c2c36bb24c) by [@cipolleschi](https://github.com/cipolleschi))
- **Text:** Fix selectable prop not working correctly ([f004cd39bc](https://github.com/facebook/react-native/commit/f004cd39bc4b632006085cbcf61df52bc5d25242) by [@iamAbhi-916](https://github.com/iamAbhi-916))
- **TextInput:** Update TextInput recycling logic to clean up the `inputAccessoryView` dependency. ([eb08f54594](https://github.com/facebook/react-native/commit/eb08f545948de9e2eca91ab3cb7569670c553b15) by [@ArturKalach](https://github.com/ArturKalach))
- **TextInput:** Fixed TextInput behavior when `maxLength={null}` is passed ([56ad53cb14](https://github.com/facebook/react-native/commit/56ad53cb14b5c842714fcf976b6ba81f68c140f2) by [@cipolleschi](https://github.com/cipolleschi))
- **View:** Inline `View` alignment with `lineHeight` in Text ([6da351a5ed](https://github.com/facebook/react-native/commit/6da351a5ed80a10138a5558afcb380410c8a93c9) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
### Security
- **Network:** Fixed vulnerability on undici and on-headers ([dd00c9055a](https://github.com/facebook/react-native/commit/dd00c9055a8f0c9ceac1716385a8a9874f7a4c2e) by [@cipolleschi](https://github.com/cipolleschi))
## v0.81.1
### Added
#### iOS specific
- **Prebuild:** Added setting SWIFT_ENABLE_EXPLICIT_MODULES=NO when using precompiled to support Xcode 26 ([939a75b5ce](https://github.com/facebook/react-native/commit/939a75b5ce2a580ece4a62689582ea81480c3e97) by [@chrfalch](https://github.com/chrfalch))
### Fixed
- **Infra:** Add missing Babel dependencies ([bf2c3af93b](https://github.com/facebook/react-native/commit/bf2c3af93b146943cb35866fa9badcd188e63f5b) by [@tido64](https://github.com/tido64))
- **Accessibility:** fix `aria-label` on `TextInput` ([6965d57e75](https://github.com/facebook/react-native/commit/6965d57e75ed0cf9f265c6020d478ddb9af4bf10) by [@mdjastrzebski](https://github.com/mdjastrzebski))
#### iOS specific
- **TextInput:** Setting maxLength to 0 in TextInput now correctly blocks typing ([c5956da8c0](https://github.com/facebook/react-native/commit/c5956da8c0b735d47761af51019ed25b49001c00) by [@riteshshukla04](https://github.com/riteshshukla04))
- **Switch:** Fix Switch layout to work with iOS 26 ([ba51aeaa90](https://github.com/facebook/react-native/commit/ba51aeaa9040014e1d77c93158c96e9bf09940cf) by [@cipolleschi](https://github.com/cipolleschi))
- **C++:** Fix import RuntimeExecutor.h with USE_FRAMEWORKS ([dacd8f26fd](https://github.com/facebook/react-native/commit/dacd8f26fda61b16b52a4953267f2108181c3282) by [@sharifhh](https://github.com/sharifhh))
- **Infra:** Fix scripts for paths containing whitespaces ([94623ca8ec](https://github.com/facebook/react-native/commit/94623ca8ec969f09d8ec430e7633c3bf49a3d71e) by [@kitten](https://github.com/kitten))
- **Prebuild:** Fixed how we copy and build the Symbols folder when precompiling ReactNativeDependencies ([a843119ff1](https://github.com/facebook/react-native/commit/a843119ff1f0e2dfb1d3884ccf255784e3cea1a7) by [@chrfalch](https://github.com/chrfalch))
- **Prebuild:** Fixed wrong jsi symbols in use when using React.xcframework ([8a2e7efe01](https://github.com/facebook/react-native/commit/8a2e7efe010c49a293c146654094b1cb5d6e6acd) by [@chrfalch](https://github.com/chrfalch))
- **Prebuild:** Fixed copying bundles correctly to xcframeworks when precompiling ReactNativeDependencies.xcframework ([e3adf47214](https://github.com/facebook/react-native/commit/e3adf4721467557f19e6cd7a65c4e2314796bc17) by [@chrfalch](https://github.com/chrfalch))
- **Prebuild:** Aligned Symbols folder in React.xcframework symbols with ReactNativeDependencies.xcframework symbols. ([8c444f773a](https://github.com/facebook/react-native/commit/8c444f773a44e8554745c9cfc1451083c12b00e3) by [@chrfalch](https://github.com/chrfalch))
- **Prebuild:** Fix "file exists" error in `ReactNativeDependencies.podspec` ([4c570b5d31](https://github.com/facebook/react-native/commit/4c570b5d31ef46e04e5fa26fa92d7f7090bf15e2) by [@vonovak](https://github.com/vonovak))
- **Prebuild:** added explicit handling of ReactCodegen ([6526a98d68](https://github.com/facebook/react-native/commit/6526a98d68dbc8578ea15cbf117c0a216c6e9af0) by [@cipolleschi](https://github.com/cipolleschi))
## v0.81.0
### Breaking
+2 -5
View File
@@ -17,13 +17,10 @@
<img src="https://img.shields.io/npm/v/react-native?color=brightgreen&label=npm%20package" alt="Current npm package version." />
</a>
<a href="https://reactnative.dev/docs/contributing">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs are welcome!" />
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome!" />
</a>
<a href="https://twitter.com/intent/follow?screen_name=reactnative">
<img src="https://img.shields.io/twitter/follow/reactnative.svg?label=Follow%20@reactnative" alt="Follow @reactnative on X" />
</a>
<a href="https://bsky.app/profile/reactnative.dev">
<img src="https://img.shields.io/badge/Bluesky-0285FF?logo=bluesky&logoColor=fff" alt="Follow @reactnative.dev on Bluesky" />
<img src="https://img.shields.io/twitter/follow/reactnative.svg?label=Follow%20@reactnative" alt="Follow @reactnative" />
</a>
</p>
-1
View File
@@ -135,7 +135,6 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(project(":packages:react-native:ReactAndroid:hermes-engine"))
// TODO: T237406039 update coordinates
.using(module("com.facebook.react:hermes-android:0.+"))
.because("Users opted to use hermes from nightly")
}
-46
View File
@@ -1,46 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare module '@expo/spawn-async' {
type SpawnOptions = {
cwd?: string,
env?: Object,
argv0?: string,
stdio?: string | Array<any>,
detached?: boolean,
uid?: number,
gid?: number,
shell?: boolean | string,
windowsVerbatimArguments?: boolean,
windowsHide?: boolean,
encoding?: string,
ignoreStdio?: boolean,
};
declare class SpawnPromise<T> extends Promise<T> {
child: child_process$ChildProcess;
}
type SpawnResult = {
pid?: number,
output: string[],
stdout: string,
stderr: string,
status: number | null,
signal: string | null,
};
declare function spawnAsync(
command: string,
args?: $ReadOnlyArray<string>,
options?: SpawnOptions,
): SpawnPromise<SpawnResult>;
declare module.exports: typeof spawnAsync;
}
-61
View File
@@ -1,61 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
// Partial types for Octokit based on the usage in react-native-github
declare module '@octokit/rest' {
declare class Octokit {
constructor(options?: {auth?: string, ...}): this;
repos: $ReadOnly<{
listReleaseAssets: (
params: $ReadOnly<{
owner: string,
repo: string,
release_id: string,
}>,
) => Promise<{
data: Array<{
id: string,
name: string,
...
}>,
...
}>,
uploadReleaseAsset: (
params: $ReadOnly<{
owner: string,
repo: string,
release_id: string,
name: string,
data: Buffer,
headers: $ReadOnly<{
'content-type': string,
...
}>,
...
}>,
) => Promise<{
data: {
browser_download_url: string,
...
},
...
}>,
deleteReleaseAsset: (params: {
owner: string,
repo: string,
asset_id: string,
...
}) => Promise<mixed>,
}>;
}
declare export {Octokit};
}
-41
View File
@@ -1,41 +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
*/
declare module 'electron-store' {
declare export type Schema = any;
declare export type Options = {
+name?: string,
defaults?: Object,
schema?: any,
migrations?: any,
beforeEachMigration?: any,
clearInvalidConfig?: boolean,
serialize?: any,
deserialize?: any,
accessPropertiesByDotNotation?: boolean,
watch?: boolean,
encryptionKey?: string | Buffer | $ReadOnlyArray<number>,
...
};
declare class ElectronStore {
constructor(options?: Options): this;
get(key: string): any;
get(key: string, defaultValue: any): any;
set(key: string, value: any): void;
set(object: Object): void;
has(key: string): boolean;
delete(key: string): void;
clear(): void;
}
declare module.exports: Class<ElectronStore>;
}
-13
View File
@@ -1,13 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare module 'fb-dotslash' {
declare module.exports: string;
}
-421
View File
@@ -1,421 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare module 'jsonc-parser' {
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
declare export const createScanner: (
text: string,
ignoreTrivia?: boolean,
) => JSONScanner;
export type ScanError = number;
export type SyntaxKind = number;
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export type JSONScanner = $ReadOnly<{
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): void,
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind,
/**
* Returns the zero-based current scan position, which is after the last read token.
*/
getPosition(): number,
/**
* Returns the last read token.
*/
getToken(): SyntaxKind,
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string,
/**
* The zero-based start offset of the last read token.
*/
getTokenOffset(): number,
/**
* The length of the last read token.
*/
getTokenLength(): number,
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number,
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number,
/**
* An error code of the last scan.
*/
getTokenError(): ScanError,
}>;
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
declare export const getLocation: (
text: string,
position: number,
) => Location;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
declare export const parse: (
text: string,
errors?: ParseError[],
options?: ParseOptions,
) => any;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
declare export const parseTree: (
text: string,
errors?: ParseError[],
options?: ParseOptions,
) => Node | void;
/**
* Finds the node at the given path in a JSON DOM.
*/
declare export const findNodeAtLocation: (
root: Node,
path: JSONPath,
) => Node | void;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
declare export const findNodeAtOffset: (
root: Node,
offset: number,
includeRightBound?: boolean,
) => Node | void;
/**
* Gets the JSON path of the given JSON DOM node
*/
declare export const getNodePath: (node: Node) => JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
declare export const getNodeValue: (node: Node) => any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
declare export const visit: (
text: string,
visitor: JSONVisitor,
options?: ParseOptions,
) => any;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
declare export const stripComments: (
text: string,
replaceCh?: string,
) => string;
export type ParseError = {
error: ParseErrorCode,
offset: number,
length: number,
};
export type ParseErrorCode = number;
declare export function printParseErrorCode(
code: ParseErrorCode,
):
| 'InvalidSymbol'
| 'InvalidNumberFormat'
| 'PropertyNameExpected'
| 'ValueExpected'
| 'ColonExpected'
| 'CommaExpected'
| 'CloseBraceExpected'
| 'CloseBracketExpected'
| 'EndOfFileExpected'
| 'InvalidCommentToken'
| 'UnexpectedEndOfComment'
| 'UnexpectedEndOfString'
| 'UnexpectedEndOfNumber'
| 'InvalidUnicode'
| 'InvalidEscapeCharacter'
| 'InvalidCharacter'
| '<unknown ParseErrorCode>';
export type NodeType =
| 'object'
| 'array'
| 'property'
| 'string'
| 'number'
| 'boolean'
| 'null';
export type Node = {
type: NodeType,
value?: any,
offset: number,
length: number,
colonOffset?: number,
parent?: Node,
children?: Node[],
};
/**
* A {@linkcode JSONPath} segment. Either a string representing an object property name
* or a number (starting at 0) for array indices.
*/
export type Segment = string | number;
export type JSONPath = Segment[];
export type Location = {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node,
/**
* The path describing the location in the JSON document. The path consists of a sequence of strings
* representing an object property or numbers for array indices.
*/
path: JSONPath,
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment of any property name or index.
* '**' will match a sequence of segments of any property name or index, or no segment.
*/
matches: (patterns: JSONPath) => boolean,
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean,
};
export type ParseOptions = {
disallowComments?: boolean,
allowTrailingComma?: boolean,
allowEmptyContent?: boolean,
};
/**
* Visitor called by {@linkcode visit} when parsing JSON.
*
* The visitor functions have the following common parameters:
* - `offset`: Global offset within the JSON document, starting at 0
* - `startLine`: Line number, starting at 0
* - `startCharacter`: Start character (column) within the current line, starting at 0
*
* Additionally some functions have a `pathSupplier` parameter which can be used to obtain the
* current `JSONPath` within the document.
*/
export type JSONVisitor = {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
*/
onObjectBegin?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
* The `JSONPath` created by the `pathSupplier` refers to the enclosing JSON object, it does not include the
* property name yet.
*/
onObjectProperty?: (
property: string,
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
*/
onArrayBegin?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (
value: any,
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (
character: string,
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* Invoked on an error.
*/
onError?: (
error: ParseErrorCode,
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
};
/**
* An edit result describes a textual edit operation. It is the result of a {@linkcode format} and {@linkcode modify} operation.
* It consist of one or more edits describing insertions, replacements or removals of text segments.
* * The offsets of the edits refer to the original state of the document.
* * No two edits change or remove the same range of text in the original document.
* * Multiple edits can have the same offset if they are multiple inserts, or an insert followed by a remove or replace.
* * The order in the array defines which edit is applied first.
* To apply an edit result use {@linkcode applyEdits}.
* In general multiple EditResults must not be concatenated because they might impact each other, producing incorrect or malformed JSON data.
*/
export type EditResult = Edit[];
/**
* Represents a text modification
*/
export type Edit = {
/**
* The start offset of the modification.
*/
offset: number,
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number,
/**
* The new content. Empty content represents a *remove*.
*/
content: string,
};
/**
* A text range in the document
*/
export type Range = {
/**
* The start offset of the range.
*/
offset: number,
/**
* The length of the range. Must not be negative.
*/
length: number,
};
/**
* Options used by {@linkcode format} when computing the formatting edit operations
*/
export type FormattingOptions = $ReadOnly<{
/**
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
*/
tabSize?: number,
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean,
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string,
}>;
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
declare export function format(
documentText: string,
range: Range | void,
options: FormattingOptions,
): EditResult;
/**
* Options used by {@linkcode modify} when computing the modification edit operations
*/
export type ModificationOptions = {
/**
* Formatting options.
*/
formattingOptions: FormattingOptions,
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number,
};
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
declare export function modify(
text: string,
path: JSONPath,
value: any,
options: ModificationOptions,
): EditResult;
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
declare export function applyEdits(text: string, edits: EditResult): string;
}
-3
View File
@@ -12,6 +12,3 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Controls whether to use Hermes from nightly builds. This will speed up builds
# but should NOT be turned on for CI or release builds.
react.internal.useHermesNightly=false
# Controls whether to use Hermes 1.0. Clean and rebuild when changing.
hermesV1Enabled=false
+5 -10
View File
@@ -54,16 +54,13 @@
"@babel/preset-env": "^7.25.3",
"@babel/preset-flow": "^7.24.7",
"@electron/packager": "^18.3.6",
"@expo/spawn-async": "^1.7.2",
"@jest/create-cache-key-function": "^29.7.0",
"@microsoft/api-extractor": "^7.52.2",
"@octokit/rest": "^22.0.0",
"@react-native/metro-babel-transformer": "0.82.0-main",
"@react-native/metro-config": "0.82.0-main",
"@tsconfig/node22": "22.0.2",
"@types/react": "^19.1.0",
"@typescript-eslint/parser": "^8.36.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.2.1",
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
"babel-plugin-syntax-hermes-parser": "0.32.0",
@@ -84,9 +81,8 @@
"eslint-plugin-react-native": "^4.0.0",
"eslint-plugin-redundant-undefined": "^0.4.0",
"eslint-plugin-relay": "^1.8.3",
"fb-dotslash": "0.5.8",
"flow-api-translator": "0.32.0",
"flow-bin": "^0.281.0",
"flow-bin": "^0.279.0",
"glob": "^7.1.1",
"hermes-eslint": "0.32.0",
"hermes-transform": "0.32.0",
@@ -95,12 +91,11 @@
"jest": "^29.7.0",
"jest-config": "^29.7.0",
"jest-diff": "^29.7.0",
"jest-junit": "^16.0.0",
"jest-junit": "^10.0.0",
"jest-snapshot": "^29.7.0",
"jsonc-parser": "2.2.1",
"markdownlint-cli2": "^0.17.2",
"markdownlint-rule-relative-links": "^3.0.0",
"memfs": "^4.38.2",
"memfs": "^4.7.7",
"metro-babel-register": "^0.83.1",
"metro-transform-plugins": "^0.83.1",
"micromatch": "^4.0.4",
@@ -112,12 +107,12 @@
"react-test-renderer": "19.1.1",
"rimraf": "^3.0.2",
"shelljs": "^0.8.5",
"signedsource": "^2.0.0",
"signedsource": "^1.0.0",
"supports-color": "^7.1.0",
"temp-dir": "^2.0.0",
"tinybench": "^4.1.0",
"typescript": "5.8.3",
"ws": "^7.5.10"
"ws": "^6.2.3"
},
"resolutions": {
"eslint-plugin-react-hooks": "6.1.0-canary-12bc60f5-20250613",
@@ -81,147 +81,10 @@ export {Commands};
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
`;
const COMMANDS_WITH_COVERAGE_INVALID = `
// @flow
const codegenNativeComponent = require('codegenNativeComponent');
import type {NativeComponentType} from 'codegenNativeComponent';
import type {ViewProps} from 'ViewPropTypes';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
// Coverage instrumentation of invalid Commands export - should still fail
export const Commands = (cov_1234567890().s[0]++, {
hotspotUpdate: () => {},
scrollTo: () => {},
});
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
`;
const COMMANDS_WITH_COVERAGE_WRONG_FUNCTION = `
// @flow
const codegenNativeComponent = require('codegenNativeComponent');
import type {NativeComponentType} from 'codegenNativeComponent';
import type {ViewProps} from 'ViewPropTypes';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
// Coverage instrumentation of wrong function call - should fail
export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({
supportedCommands: ['pause', 'play'],
}));
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
`;
const COMMANDS_WITH_COMPLEX_COVERAGE_INVALID = `
// @flow
const codegenNativeComponent = require('codegenNativeComponent');
import type {NativeComponentType} from 'codegenNativeComponent';
import type {ViewProps} from 'ViewPropTypes';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
// Complex coverage instrumentation with invalid nested structure - should fail
export const Commands = (
cov_xyz789().f[1]++,
cov_xyz789().s[2]++,
{
pause: (ref) => {},
play: (ref) => {},
}
);
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
`;
const COMMANDS_WITH_COVERAGE_WRONG_NAME = `
// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {NativeComponentType} from 'codegenNativeComponent';
import type {ViewProps} from 'ViewPropTypes';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+pause: (viewRef: React.ElementRef<NativeType>) => void;
+play: (viewRef: React.ElementRef<NativeType>) => void;
}
// Coverage instrumentation with correct function but wrong export name - should fail
export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands<NativeCommands>({
supportedCommands: ['pause', 'play'],
}));
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
`;
const COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID = `
// @flow
const codegenNativeComponent = require('codegenNativeComponent');
import type {NativeComponentType} from 'codegenNativeComponent';
import type {ViewProps} from 'ViewPropTypes';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+pause: (viewRef: React.ElementRef<NativeType>) => void;
+play: (viewRef: React.ElementRef<NativeType>) => void;
}
// Coverage instrumentation with type cast but wrong function - should fail
export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({
supportedCommands: ['pause', 'play'],
}));
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
`;
module.exports = {
'CommandsExportedWithDifferentNameNativeComponent.js':
COMMANDS_EXPORTED_WITH_DIFFERENT_NAME,
'CommandsExportedWithShorthandNativeComponent.js':
COMMANDS_EXPORTED_WITH_SHORTHAND,
'OtherCommandsExportNativeComponent.js': OTHER_COMMANDS_EXPORT,
'CommandsWithCoverageInvalidNativeComponent.js':
COMMANDS_WITH_COVERAGE_INVALID,
'CommandsWithCoverageWrongFunctionNativeComponent.js':
COMMANDS_WITH_COVERAGE_WRONG_FUNCTION,
'CommandsWithComplexCoverageInvalidNativeComponent.js':
COMMANDS_WITH_COMPLEX_COVERAGE_INVALID,
'CommandsWithCoverageWrongNameNativeComponent.js':
COMMANDS_WITH_COVERAGE_WRONG_NAME,
'CommandsWithCoverageTypeCastInvalidNativeComponent.js':
COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID,
};
@@ -59,92 +59,6 @@ export default codegenNativeComponent<ModuleProps>('Module', {
});
`;
// Coverage instrumentation test cases - should be recognized as valid
const COMMANDS_WITH_SIMPLE_COVERAGE = `
// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {NativeComponentType} from 'codegenNativeComponent';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+pause: (viewRef: React.ElementRef<NativeType>) => void;
+play: (viewRef: React.ElementRef<NativeType>) => void;
}
export const Commands = (cov_1234567890.s[0]++, codegenNativeCommands<NativeCommands>({
supportedCommands: ['pause', 'play'],
}));
export default codegenNativeComponent<ModuleProps>('Module');
`;
const COMMANDS_WITH_COMPLEX_COVERAGE = `
// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {NativeComponentType} from 'codegenNativeComponent';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+seek: (viewRef: React.ElementRef<NativeType>, position: number) => void;
+stop: (viewRef: React.ElementRef<NativeType>) => void;
}
export const Commands = (
cov_abcdef123().f[2]++,
cov_abcdef123().s[5]++,
codegenNativeCommands<NativeCommands>({
supportedCommands: ['seek', 'stop'],
})
);
export default codegenNativeComponent<ModuleProps>('Module');
`;
const COMMANDS_WITH_TYPE_CAST_COVERAGE = `
// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {NativeComponentType} from 'codegenNativeComponent';
type ModuleProps = $ReadOnly<{|
...ViewProps,
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+mute: (viewRef: React.ElementRef<NativeType>) => void;
+unmute: (viewRef: React.ElementRef<NativeType>) => void;
}
export const Commands: NativeCommands = (cov_xyz789().s[1]++, codegenNativeCommands<NativeCommands>({
supportedCommands: ['mute', 'unmute'],
}));
export default codegenNativeComponent<ModuleProps>('Module');
`;
const FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT = `
// @flow
@@ -193,9 +107,4 @@ module.exports = {
'NotANativeComponent.js': NOT_A_NATIVE_COMPONENT,
'FullNativeComponent.js': FULL_NATIVE_COMPONENT,
'FullTypedNativeComponent.js': FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT,
'CommandsWithSimpleCoverageNativeComponent.js': COMMANDS_WITH_SIMPLE_COVERAGE,
'CommandsWithComplexCoverageNativeComponent.js':
COMMANDS_WITH_COMPLEX_COVERAGE,
'CommandsWithTypeCastCoverageNativeComponent.js':
COMMANDS_WITH_TYPE_CAST_COVERAGE,
};
@@ -1,77 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Babel plugin inline view configs can inline config for CommandsWithComplexCoverageNativeComponent.js 1`] = `
"// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type { ViewProps } from 'ViewPropTypes';
import type { NativeComponentType } from 'codegenNativeComponent';
type ModuleProps = $ReadOnly<{|
...ViewProps
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+seek: (viewRef: React.ElementRef<NativeType>, position: number) => void,
+stop: (viewRef: React.ElementRef<NativeType>) => void,
}
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
let nativeComponentName = 'Module';
export const __INTERNAL_VIEW_CONFIG = {
uiViewClassName: \\"Module\\",
validAttributes: {}
};
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
`;
exports[`Babel plugin inline view configs can inline config for CommandsWithSimpleCoverageNativeComponent.js 1`] = `
"// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type { ViewProps } from 'ViewPropTypes';
import type { NativeComponentType } from 'codegenNativeComponent';
type ModuleProps = $ReadOnly<{|
...ViewProps
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+pause: (viewRef: React.ElementRef<NativeType>) => void,
+play: (viewRef: React.ElementRef<NativeType>) => void,
}
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
let nativeComponentName = 'Module';
export const __INTERNAL_VIEW_CONFIG = {
uiViewClassName: \\"Module\\",
validAttributes: {}
};
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
`;
exports[`Babel plugin inline view configs can inline config for CommandsWithTypeCastCoverageNativeComponent.js 1`] = `
"// @flow
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type { ViewProps } from 'ViewPropTypes';
import type { NativeComponentType } from 'codegenNativeComponent';
type ModuleProps = $ReadOnly<{|
...ViewProps
|}>;
type NativeType = NativeComponentType<ModuleProps>;
interface NativeCommands {
+mute: (viewRef: React.ElementRef<NativeType>) => void,
+unmute: (viewRef: React.ElementRef<NativeType>) => void,
}
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
let nativeComponentName = 'Module';
export const __INTERNAL_VIEW_CONFIG = {
uiViewClassName: \\"Module\\",
validAttributes: {}
};
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
`;
exports[`Babel plugin inline view configs can inline config for FullNativeComponent.js 1`] = `
"// @flow
@@ -225,61 +153,6 @@ exports[`Babel plugin inline view configs fails on inline config for CommandsExp
24 |"
`;
exports[`Babel plugin inline view configs fails on inline config for CommandsWithComplexCoverageInvalidNativeComponent.js 1`] = `
"/CommandsWithComplexCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
14 |
15 | // Complex coverage instrumentation with invalid nested structure - should fail
> 16 | export const Commands = (
| ^
17 | cov_xyz789().f[1]++,
18 | cov_xyz789().s[2]++,
19 | {"
`;
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageInvalidNativeComponent.js 1`] = `
"/CommandsWithCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
14 |
15 | // Coverage instrumentation of invalid Commands export - should still fail
> 16 | export const Commands = (cov_1234567890().s[0]++, {
| ^
17 | hotspotUpdate: () => {},
18 | scrollTo: () => {},
19 | });"
`;
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageTypeCastInvalidNativeComponent.js 1`] = `
"/CommandsWithCoverageTypeCastInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
19 |
20 | // Coverage instrumentation with type cast but wrong function - should fail
> 21 | export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({
| ^
22 | supportedCommands: ['pause', 'play'],
23 | }));
24 |"
`;
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongFunctionNativeComponent.js 1`] = `
"/CommandsWithCoverageWrongFunctionNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
14 |
15 | // Coverage instrumentation of wrong function call - should fail
> 16 | export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({
| ^
17 | supportedCommands: ['pause', 'play'],
18 | }));
19 |"
`;
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongNameNativeComponent.js 1`] = `
"/CommandsWithCoverageWrongNameNativeComponent.js: Native commands must be exported with the name 'Commands'
20 |
21 | // Coverage instrumentation with correct function but wrong export name - should fail
> 22 | export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands<NativeCommands>({
| ^
23 | supportedCommands: ['pause', 'play'],
24 | }));
25 |"
`;
exports[`Babel plugin inline view configs fails on inline config for OtherCommandsExportNativeComponent.js 1`] = `
"/OtherCommandsExportNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
17 | }
+6 -58
View File
@@ -102,58 +102,6 @@ function isCodegenDeclaration(declaration) {
return false;
}
function isCodegenNativeCommandsDeclaration(declaration) {
if (!declaration) {
return false;
}
// Handle direct calls: codegenNativeCommands()
if (
declaration.type === 'CallExpression' &&
declaration.callee &&
declaration.callee.type === 'Identifier' &&
declaration.callee.name === 'codegenNativeCommands'
) {
return true;
}
// Handle coverage instrumentation: (cov_xxx().s[0]++, codegenNativeCommands())
if (declaration.type === 'SequenceExpression' && declaration.expressions) {
// Get the last expression in the sequence (the actual function call)
const lastExpression =
declaration.expressions[declaration.expressions.length - 1];
// Recursively check if the last expression is a valid codegenNativeCommands call
return isCodegenNativeCommandsDeclaration(lastExpression);
}
// Handle Flow type casts: (codegenNativeCommands(): NativeCommands)
if (
(declaration.type === 'TypeCastExpression' ||
declaration.type === 'AsExpression') &&
declaration.expression &&
declaration.expression.type === 'CallExpression' &&
declaration.expression.callee &&
declaration.expression.callee.type === 'Identifier' &&
declaration.expression.callee.name === 'codegenNativeCommands'
) {
return true;
}
// Handle TypeScript assertions: codegenNativeCommands() as NativeCommands
if (
declaration.type === 'TSAsExpression' &&
declaration.expression &&
declaration.expression.type === 'CallExpression' &&
declaration.expression.callee &&
declaration.expression.callee.type === 'Identifier' &&
declaration.expression.callee.name === 'codegenNativeCommands'
) {
return true;
}
return false;
}
module.exports = function ({parse, types: t}) {
return {
pre(state) {
@@ -177,12 +125,12 @@ module.exports = function ({parse, types: t}) {
const firstDeclaration = path.node.declaration.declarations[0];
if (firstDeclaration.type === 'VariableDeclarator') {
// Check if this is a valid codegenNativeCommands call, handling type annotations
const isValidCommandsExport = isCodegenNativeCommandsDeclaration(
firstDeclaration.init,
);
if (isValidCommandsExport) {
if (
firstDeclaration.init &&
firstDeclaration.init.type === 'CallExpression' &&
firstDeclaration.init.callee.type === 'Identifier' &&
firstDeclaration.init.callee.name === 'codegenNativeCommands'
) {
if (
firstDeclaration.id.type === 'Identifier' &&
firstDeclaration.id.name !== 'Commands'
@@ -40,9 +40,6 @@
"peerDependenciesMeta": {
"@react-native-community/cli": {
"optional": true
},
"@react-native/metro-config": {
"optional": true
}
},
"engines": {
@@ -31,9 +31,9 @@ type MiddlewareReturn = {
...
};
// $FlowFixMe[incompatible-type]
// $FlowFixMe
const unusedStubWSServer: ws$WebSocketServer = {};
// $FlowFixMe[incompatible-type]
// $FlowFixMe
const unusedMiddlewareStub: Server = {};
const communityMiddlewareFallback = {
@@ -88,6 +88,7 @@ Diff: ${styleText(['dim', 'underline'], newVersion?.diffUrl ?? 'none')}
}
}
// $FlowFixMe
function isDiffPurgeEntry(data: Partial<DiffPurge>): data is DiffPurge {
return (
// $FlowFixMe[incompatible-type-guard]
+2 -2
View File
@@ -1,5 +1,5 @@
@generated SignedSource<<0b54f75686e4893a5444839bf621a317>>
Git revision: 5a792db1225adda206313e9bf751198a1ca7851a
@generated SignedSource<<b6a779e724ecce5e727ee674dc20f809>>
Git revision: e87564a24cf233c60aaebee8c418ec85724f7214
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebook/react-native-devtools-frontend
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -238,7 +238,7 @@ import"../../ui/components/icon_button/icon_button.js";import"../../ui/component
<td>${e.method}</td>
<td>${e.params?d`<code>${JSON.stringify(e.params)}</code>`:""}</td>
<td>
${e.result?d`<code>${JSON.stringify(e.result)}</code>`:e.error?d`<code>${JSON.stringify(e.error)}</code>`:"id"in e?"(pending)":""}
${e.result?d`<code>${JSON.stringify(e.result)}</code>`:e.error?d`<code>${JSON.stringify(e.error)}</code>`:"(pending)"}
</td>
<td data-value=${e.elapsedTime||0}>
${"id"in e?e.elapsedTime?U(A.sMs,{PH1:String(e.elapsedTime)}):"(pending)":""}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,33 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`prepareDebuggerShellFromDotSlashFile fails with the expected error message for a missing dotslash file 1`] = `
Object {
"code": "unexpected_error",
"humanReadableMessage": "An unexpected error occured while installing the latest version of React Native DevTools. Using a fallback version instead.",
"verboseInfo": Any<String>,
}
`;
exports[`prepareDebuggerShellFromDotSlashFile fails with the expected error message for missing platforms 1`] = `
Object {
"code": "platform_not_supported",
"humanReadableMessage": "The latest version of React Native DevTools is not supported on this platform. Using a fallback version instead.",
"verboseInfo": Any<String>,
}
`;
exports[`prepareDebuggerShellFromDotSlashFile scenarios requiring a local HTTP server fails with the expected error message for a corrupted tarball 1`] = `
Object {
"code": "possible_corruption",
"humanReadableMessage": "Failed to verify the latest version of React Native DevTools. Using a fallback version instead. ",
"verboseInfo": Any<String>,
}
`;
exports[`prepareDebuggerShellFromDotSlashFile scenarios requiring a local HTTP server fails with the expected error message for a network error 1`] = `
Object {
"code": "likely_offline",
"humanReadableMessage": "Failed to download the latest version of React Native DevTools. Using a fallback version instead. Connect to the internet or check your network settings.",
"verboseInfo": Any<String>,
}
`;
@@ -1,59 +0,0 @@
#!/usr/bin/env dotslash
{
"name": "React Native DevTools",
"platforms": {
"linux-aarch64": {
"size": 113510892,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "http://$HOST:$PORT/corrupted.tar.gz"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-arm64/React Native DevTools"
},
"linux-x86_64": {
"size": 113243910,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "http://$HOST:$PORT/corrupted.tar.gz"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-x64/React Native DevTools"
},
"macos-aarch64": {
"size": 108810433,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "http://$HOST:$PORT/corrupted.tar.gz"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
},
"macos-x86_64": {
"size": 113769989,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "http://$HOST:$PORT/corrupted.tar.gz"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
}
}
}
@@ -1,59 +0,0 @@
#!/usr/bin/env dotslash
{
"name": "React Native DevTools",
"platforms": {
"linux-aarch64": {
"size": 113510892,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "https://$HOST:$PORT/does-not-exist"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-arm64/React Native DevTools"
},
"linux-x86_64": {
"size": 113243910,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "https://$HOST:$PORT/does-not-exist"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-x64/React Native DevTools"
},
"macos-aarch64": {
"size": 108810433,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "https://$HOST:$PORT/does-not-exist"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
},
"macos-x86_64": {
"size": 113769989,
"hash": "sha256",
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"providers": [
{
"type": "http",
"url": "https://$HOST:$PORT/does-not-exist"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
}
}
}
@@ -1,6 +0,0 @@
#!/usr/bin/env dotslash
{
"name": "React Native DevTools",
"platforms": {}
}
@@ -1,139 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
const {
prepareDebuggerShellFromDotSlashFile,
} = require('../src/node/private/LaunchUtils');
const fs = require('fs').promises;
const http = require('http');
const os = require('os');
const path = require('path');
// The implementation of prepareDebuggerShellFromDotSlashFile relies on
// details of DotSlash that are not guaranteed to be stable (support for
// `dotslash -- fetch <file>`, certain strings being printed to stderr).
// This (admittedly elaborate) test suite ensures we'll fail loudly if we
// try to upgrade DotSlash to a version that breaks our assumptions.
describe('prepareDebuggerShellFromDotSlashFile', () => {
test('fails with the expected error message for missing platforms', async () => {
const result = await prepareDebuggerShellFromDotSlashFile(
path.join(__dirname, 'dotslash-file-with-missing-platforms.jsonc'),
);
expect(result).toMatchSnapshot({
verboseInfo: expect.any(String),
});
});
test('fails with the expected error message for a missing dotslash file', async () => {
const result = await prepareDebuggerShellFromDotSlashFile(
path.join(__dirname, 'dotslash-file-that-does-not-exist.jsonc'),
);
expect(result).toMatchSnapshot({
verboseInfo: expect.any(String),
});
});
describe('scenarios requiring a local HTTP server', () => {
let server, scratchDir;
beforeEach(async () => {
scratchDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dotslash-test-'));
server = http.createServer((request, response) => {
if (request.url === '/corrupted.tar.gz') {
response.writeHead(200, {'Content-Type': 'application/gzip'});
response.end(
'Hello, world!\n' + 'This simulated a corrupted tarball.',
);
} else {
response.writeHead(404);
response.end();
}
});
await new Promise((resolve, reject) => {
server.on('error', reject);
server.listen(0, 'localhost', () => {
server.removeListener('error', reject);
resolve();
});
});
});
afterEach(async () => {
await fs.rm(scratchDir, {recursive: true, force: true});
if (server.listening) {
await new Promise((resolve, reject) => {
server.close(error => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
});
test('fails with the expected error message for a corrupted tarball', async () => {
const dotslashFileContents = injectHostPort(
await fs.readFile(
path.join(
__dirname,
'dotslash-file-simulating-data-corruption.jsonc',
),
'utf8',
),
server.address(),
);
await fs.writeFile(
path.join(scratchDir, 'dotslash-file.jsonc'),
dotslashFileContents,
);
const result = await prepareDebuggerShellFromDotSlashFile(
path.join(scratchDir, 'dotslash-file.jsonc'),
);
expect(result).toMatchSnapshot({
verboseInfo: expect.any(String),
});
});
test('fails with the expected error message for a network error', async () => {
const dotslashFileContents = injectHostPort(
await fs.readFile(
path.join(__dirname, 'dotslash-file-simulating-network-error.jsonc'),
'utf8',
),
server.address(),
);
await fs.writeFile(
path.join(scratchDir, 'dotslash-file.jsonc'),
dotslashFileContents,
);
const result = await prepareDebuggerShellFromDotSlashFile(
path.join(scratchDir, 'dotslash-file.jsonc'),
);
expect(result).toMatchSnapshot({
verboseInfo: expect.any(String),
});
});
});
});
function injectHostPort(
dotslashFileContents: string,
address: net$Socket$address,
) {
const host =
address.family === 'IPv6' ? `[${address.address}]` : address.address;
return dotslashFileContents
.replaceAll('$HOST', host)
.replaceAll('$PORT', address.port.toString());
}
@@ -22,7 +22,7 @@ describe('Electron dependency', () => {
// $FlowFixMe[untyped-import] - package.json is not typed
const ourPackageJson = require('../package.json');
const declaredElectronVersion = ourPackageJson.devDependencies.electron;
const declaredElectronVersion = ourPackageJson.dependencies.electron;
expect(declaredElectronVersion).toBeTruthy();
// $FlowFixMe[untyped-import] - package.json is not typed
@@ -1,75 +1,62 @@
#!/usr/bin/env dotslash
// @generated SignedSource<<9df662721aea6e3774a8677e2a6065e4>>
// @generated SignedSource<<14de73ea0ed751d80c7c687c6aeb123f>>
{
"name": "React Native DevTools",
"platforms": {
"linux-aarch64": {
"size": 116056584,
"size": 116120331,
"hash": "sha256",
"digest": "fac3912f10e3c373c874be6c4696f11e05cbaa754c116db6ea72189afae5efe6",
"digest": "5a1747bdb50f8140d96e99b31f9a603ceaf52e777e5dd59f65edacd87f8c6348",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPdlYV_w4y_wxd5Rs8c_F-soLMbY1zwXJjD-JM4TkOtOELyxZGyreTb6dTehp_X6e3qVFZUT05ETRs0MbvqYr4mqdb6dkG9DqNQEVv3TBoHwl7TYSygtHJEs3gXTAtMLYEKW9xXODJlVF-9II7fdTdHU7x7Pyf6NR6S3nv7sKUVD-zKpxF50L2TwUQ"
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQMRq1ou56GjpsIRFHpozEYiPfCuVvgWQVwiMMPBbwKyBnebit8HDGKof5XgDHbAKCqAZSgC8L22eJETnqIUM3kEAMYNXcHviIGy41rsXKVDYDgyWlGFB1zP2WzHrjWagfD062Pt4q5GqvCG5RVlhz656BOsutU7B4pDXGFCIdry7FveKn0PXGxvd1E"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-arm64/React Native DevTools"
},
"linux-x86_64": {
"size": 115929093,
"size": 116056146,
"hash": "sha256",
"digest": "06bbaeb62ae2e0081d184eba42b9f15be0d0b3f4901142b08a8a1430ff83e722",
"digest": "b48a3e392ac482de8058917879cf07fd5934dcb84aa901d4fe0d29273b503f54",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPENbrxrg0RF_TiyxSY_YVYqC1UfRKj_bMfKb3qHUsIhBlya8n3FkbfJqTXTNdqL4riFRulXS5ecXZprtvk_9kao-zY59r3kiTRwobzF0jnjM507_9UOnEHWzG5ZJYzQyOtS3kQmT0HwZabVj9qw38OB9mk-MPVCZIPZay2PRnIThBzpKDoOOP9"
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOyyb_LXrME2ubBGSkEi1vDw-2hwuHgHwnR-kAGQyhJK7lnELQDXKX1xW_u0joDwbTOhmiptwenq07G2NFkrY3t_AXefb_xTu2qHzpmrsX2YcwJlewbprgbuX7Uvdhqncb_IRAnJ4ogYKHUg6CZBNmLCQsyDsoqmkXtJ9ikJcQeDu2aiqbb-RWJ"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-x64/React Native DevTools"
},
"macos-aarch64": {
"size": 110891603,
"size": 110957864,
"hash": "sha256",
"digest": "eeb9cc1399c0c38c429848dbf622f1b46e88d7d97788dcdc4c30a9fcce37705c",
"digest": "7964d83c857f12bb741abf4377771f3f3697b81df6283dd88d75ae43991afb73",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN9bd7nbXi-uF_MooJEiUL2jJVifMqgQxZ236aqbSdiJe5Uzjud5ANf0LDAl8GDVVRShd6x4B-gsZ-qRowH0qJikoGatQIkBgyzp4i8ors52etGOIIwdAxdNIng5Vtp441j2_N__btkJqlHcMMbbqvO8fg9oeYTlhMpOx3MLLWXIlG7ix5IFUDiYChf"
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQM7USVzavWxZkjOO6CasnSfPIfE08jJAkwfVO0qWiYBli136vpzA_HS89ZqsIsAC_GXeT7K-K9BxdON7z5qBoRMUygAay7z4DGT5OZ9YSf9MDCd0JOzah5_6s3ijw2j4eeRu8ZNYzI3aTutDXtPMJAb2KfBHGx2lEiSW5YK9idcB5Y2boAWeDXXX5nb"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
},
"macos-x86_64": {
"size": 117770388,
"size": 117827315,
"hash": "sha256",
"digest": "a65e446e526502b267cbe6800ae031e4e1b5b0aca21412152a66ffe1d3a29410",
"digest": "bab2fac43def4a82fb88300aac05180eeda24f31cc00402a3ff7ca2e612bd532",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN29ukKLZS48QcCU-gms06YFnG-fxxDfrWTup-Z6KoIAP-QEu3sFzrpqeZz4g-jFsh3o-IIoHJsdbu4a2U7ROjPfWGguGJrPK8fdc3iV67Qw1_VAPU13dm0dI1DbSY50ah05wh43jdBG3LGCAYYJ-W8_mZMC3HqM2v-8KVd-DoPT4hOh1s9CCFgEQ"
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPYS0ZRQ7RV1S-yy3tut6pRqcKdPcOiKfekrA5AXdd_M-HDaXLwJS57iAFU2K2X-EA_cyg25C3L-5L7O3eyNKPMToZHbV282GiojnNSKFNjsjj9_B737lOwHI_XqbDQEIiNJZ7NjbkZ8_iMKK-LNL-Ydha2ORWWqytuzP4sWQ6sm0XIxsdyQ6bOLw"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
},
"windows-x86_64": {
"size": 125526370,
"hash": "sha256",
"digest": "26b190c0f85249dee91999e020b8fe7ffd5c007458a2103ed3822558861dbe87",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQO0mdIJx1eYiSDJNiAZ2soaeECILayWnvOeiey-fpS4ydDTreAMfwL4QZ5GZo4-AsGwOXCSdF3hBMM2ufnvw6BDFg5UEiTc4DALJu7o6YBBG4wlVZbI-2kkaXd7u8RhBQPjmhsdcX3sDmCGmSr3WK9EkTRKStE2lb6ilOn-969h4dYWekwUUCSX"
}
],
"format": "tar.gz",
"path": "React Native DevTools-win32-x64/React Native DevTools.exe"
}
}
}
+4 -12
View File
@@ -1,6 +1,5 @@
{
"name": "@react-native/debugger-shell",
"productName": "React Native DevTools",
"version": "0.82.0-main",
"description": "Experimental debugger shell for React Native for use with @react-native/debugger-frontend",
"keywords": [
@@ -27,21 +26,14 @@
},
"license": "MIT",
"engines": {
"node": ">= 20.19.4"
"node": ">= 20.19.4",
"electron": ">=37.2.6"
},
"dependencies": {
"cross-spawn": "^7.0.6",
"fb-dotslash": "0.5.8"
"electron": "37.2.6"
},
"devDependencies": {
"electron": "37.2.6",
"electron-store": "^8.2.0",
"semver": "^7.1.3"
},
"files": [
"!**/__tests__/**",
"bin",
"dist",
"!src/electron"
]
}
}
@@ -1,13 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
export default {
revision: 'dev',
};
@@ -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.
*
* %s
* @flow strict-local
* @format
*/
'use strict';
module.exports = {
default: {
revision: %s,
},
__esModule: true,
};
@@ -9,12 +9,10 @@
*/
// $FlowFixMe[unclear-type] We have no Flow types for the Electron API.
const {BrowserWindow, Menu, app, shell, ipcMain} = require('electron') as any;
const Store = require('electron-store');
const {BrowserWindow, app, shell, ipcMain} = require('electron') as any;
const path = require('path');
const util = require('util');
const appSettings = new Store();
const windowMetadata = new WeakMap<
typeof BrowserWindow,
$ReadOnly<{
@@ -38,7 +36,7 @@ function handleLaunchArgs(argv: string[]) {
});
// Find an existing window for this app and launch configuration.
let frontendWindow = BrowserWindow.getAllWindows().find(window => {
const existingWindow = BrowserWindow.getAllWindows().find(window => {
const metadata = windowMetadata.get(window);
if (!metadata) {
return false;
@@ -46,42 +44,41 @@ function handleLaunchArgs(argv: string[]) {
return metadata.windowKey === windowKey;
});
if (frontendWindow) {
if (existingWindow) {
// If the window is already visible, flash it.
if (frontendWindow.isVisible()) {
frontendWindow.flashFrame(true);
if (existingWindow.isVisible()) {
existingWindow.flashFrame(true);
setTimeout(() => {
frontendWindow.flashFrame(false);
existingWindow.flashFrame(false);
}, 1000);
}
} else {
frontendWindow = new BrowserWindow({
...(getSavedWindowPosition(windowKey) ?? {
width: 1200,
height: 600,
}),
webPreferences: {
partition: 'persist:react-native-devtools',
preload: require.resolve('./preload.js'),
},
// Icon for Linux
icon: path.join(__dirname, 'resources', 'icon.png'),
});
// Auto-hide the Windows/Linux menu bar
frontendWindow.setMenuBarVisibility(false);
// Observe and update saved window position
setupWindowResizeListeners(frontendWindow, windowKey);
if (process.platform === 'darwin') {
app.focus({
steal: true,
});
}
existingWindow.focus();
return;
}
// Create the browser window.
const frontendWindow = new BrowserWindow({
width: 1200,
height: 600,
webPreferences: {
partition: 'persist:react-native-devtools',
preload: require.resolve('./preload.js'),
},
// Icon for Linux
icon: path.join(__dirname, 'resources', 'icon.png'),
});
// Open links in the default browser instead of in new Electron windows.
frontendWindow.webContents.setWindowOpenHandler(({url}) => {
shell.openExternal(url);
return {action: 'deny'};
});
// TODO: If the window contains a live, working frontend instance with a valid connection to the backend,
// we should avoid this reload and instead send the frontend a message to handle the launch arguments
// dynamically (e.g. update the launch ID for telemetry purposes, handle deeplinking to a specific CDT panel, etc).
frontendWindow.loadURL(frontendUrl);
windowMetadata.set(frontendWindow, {
@@ -93,71 +90,10 @@ function handleLaunchArgs(argv: string[]) {
steal: true,
});
}
frontendWindow.focus();
}
function configureAppMenu() {
const template = [
...(process.platform === 'darwin' ? [{role: 'appMenu'}] : []),
{role: 'fileMenu'},
{role: 'editMenu'},
{role: 'viewMenu'},
{role: 'windowMenu'},
{
role: 'help',
submenu: [
{
label: 'React Native Website',
click: () => shell.openExternal('https://reactnative.dev'),
},
{
label: 'Release Notes',
click: () =>
shell.openExternal(
'https://github.com/facebook/react-native/releases',
),
},
],
},
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
function getSavedWindowPosition(
windowKey: string,
): ?{width: number, height: number, x?: number, y?: number} {
return appSettings.get('windowArrangements', {})[windowKey];
}
function saveWindowPosition(
windowKey: string,
position: {x: number, y: number, width: number, height: number},
) {
const windowArrangements = appSettings.get('windowArrangements', {});
windowArrangements[windowKey] = position;
appSettings.set('windowArrangements', windowArrangements);
}
function setupWindowResizeListeners(
browserWindow: typeof BrowserWindow,
windowKey: string,
) {
const savePosition = () => {
if (!browserWindow.isDestroyed()) {
const [x, y] = browserWindow.getPosition();
const [width, height] = browserWindow.getSize();
saveWindowPosition(windowKey, {x, y, width, height});
}
};
browserWindow.on('moved', savePosition);
browserWindow.on('resized', savePosition);
browserWindow.on('closed', savePosition);
}
app.whenReady().then(() => {
handleLaunchArgs(process.argv.slice(app.isPackaged ? 1 : 2));
configureAppMenu();
app.on(
'second-instance',
@@ -8,17 +8,9 @@
* @format
*/
import buildInfo from './BuildInfo';
// $FlowFixMe[untyped-import] Flow doesn't infer JSON types
const pkg = require('../../package.json');
const util = require('util');
// $FlowFixMe[unclear-type] We have no Flow types for the Electron API.
const {app} = require('electron') as any;
// Set the application name and version
app.setName(pkg.productName ?? pkg.name);
app.setVersion(pkg.version + '-' + buildInfo.revision);
const util = require('util');
// Handle global command line arguments which don't require a window
// or the single instance lock to be held.
@@ -30,7 +22,7 @@ const {
strict: false,
});
if (version) {
console.log(`${pkg.name} v${app.getVersion()}`);
console.log(`${app.getName()} v${app.getVersion()}`);
// Not app.quit() - we want to exit immediately without initialising the graphical subsystem.
app.exit(0);
}
@@ -1,48 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
const {unstable_spawnDebuggerShellWithArgs} = require('../../');
describe('debugger-shell Node package', () => {
test('can spawn in detached+prebuilt mode without crashing', async () => {
await expect(
unstable_spawnDebuggerShellWithArgs(['--version'], {
flavor: 'prebuilt',
mode: 'detached',
}),
).resolves.toBeUndefined();
});
// When running in the internal react-native-oss-js job, Electron isn't
// installed correctly (postinstall scripts don't run) but the internal
// `electron` workspace isn't available either. Detecting this dynamically
// weakens the test somewhat in environments where it *should* pass, but this
// is a dev-only feature anyway so this is fine.
if (isElectronInstalled()) {
test('can spawn in detached+dev mode without crashing', async () => {
await expect(
unstable_spawnDebuggerShellWithArgs(['--version'], {
flavor: 'dev',
mode: 'detached',
}),
).resolves.toBeUndefined();
});
}
});
function isElectronInstalled() {
try {
require('electron');
return true;
} catch {
return false;
}
}
+17 -110
View File
@@ -8,54 +8,44 @@
* @format
*/
import {
prepareDebuggerShellFromDotSlashFile,
spawnAndGetStderr,
} from './private/LaunchUtils';
const {spawn} = require('cross-spawn');
const path = require('path');
// The 'prebuilt' flavor will use the prebuilt shell binary (and the JavaScript embedded in it).
// The 'dev' flavor will use a stock Electron binary and run the shell code from the `electron/` directory.
type DebuggerShellFlavor = 'prebuilt' | 'dev';
const DEVTOOLS_BINARY_DOTSLASH_FILE = path.join(
__dirname,
'../../bin/react-native-devtools',
);
async function unstable_spawnDebuggerShellWithArgs(
args: string[],
{
mode = 'detached',
flavor = 'prebuilt',
}: $ReadOnly<{
// In 'syncAndExit' mode, the current process will block until the spawned process exits, and then it will exit
// with the same exit code as the spawned process.
// In 'detached' mode, the spawned process will be detached from the current process and the current process will
// continue to run normally.
mode?: 'syncThenExit' | 'detached',
flavor?: DebuggerShellFlavor,
}> = {},
): Promise<void> {
const [binaryPath, baseArgs] = getShellBinaryAndArgs(flavor);
// NOTE: Internally at Meta, this is aliased to a workspace that is
// API-compatible with the 'electron' package, but contains prebuilt binaries
// that do not need to be downloaded in a postinstall action.
const electronPath = require('electron');
return new Promise((resolve, reject) => {
const child = spawn(binaryPath, [...baseArgs, ...args], {
stdio: 'inherit',
windowsHide: true,
detached: mode === 'detached',
});
const child = spawn(
electronPath,
[require.resolve('../electron'), ...args],
{
stdio: 'inherit',
windowsHide: true,
detached: mode === 'detached',
},
);
if (mode === 'detached') {
child.on('spawn', () => {
resolve();
});
child.on('close', (code: number) => {
child.on('close', (code /*: number */) => {
if (code !== 0) {
reject(
new Error(
`Failed to open debugger shell: exited with code ${code}`,
`Failed to open debugger shell: ${electronPath} exited with code ${code}`,
),
);
}
@@ -64,7 +54,7 @@ async function unstable_spawnDebuggerShellWithArgs(
} else if (mode === 'syncThenExit') {
child.on('close', function (code, signal) {
if (code === null) {
console.error('Debugger shell exited with signal', signal);
console.error(electronPath, 'exited with signal', signal);
process.exit(1);
}
process.exit(code);
@@ -84,87 +74,4 @@ async function unstable_spawnDebuggerShellWithArgs(
});
}
export type DebuggerShellPreparationResult = $ReadOnly<{
code:
| 'success'
| 'not_implemented'
| 'likely_offline'
| 'platform_not_supported'
| 'possible_corruption'
| 'unexpected_error',
humanReadableMessage?: string,
verboseInfo?: string,
}>;
/**
* Attempts to prepare the debugger shell for use and returns a coded result
* that can be used to advise the user on how to proceed in case of failure.
* In particular, this function will attempt to download and extract an
* appropriate binary for the "prebuilt" flavor.
*
* This function should be called early during dev server startup, in parallel
* with other initialization steps, so that the debugger shell is ready to use
* instantly when the user tries to open it (and conversely, the user is
* informed ASAP if it is not ready to use).
*/
async function unstable_prepareDebuggerShell(
flavor: DebuggerShellFlavor,
): Promise<DebuggerShellPreparationResult> {
const [binaryPath, baseArgs] = getShellBinaryAndArgs(flavor);
try {
switch (flavor) {
case 'prebuilt':
const prebuiltResult = await prepareDebuggerShellFromDotSlashFile(
DEVTOOLS_BINARY_DOTSLASH_FILE,
);
if (prebuiltResult.code !== 'success') {
return prebuiltResult;
}
break;
case 'dev':
break;
default:
flavor as empty;
throw new Error(`Unknown flavor: ${flavor}`);
}
const {code, stderr} = await spawnAndGetStderr(binaryPath, [
...baseArgs,
'--version',
]);
if (code !== 0) {
return {
code: 'unexpected_error',
verboseInfo: stderr,
};
}
return {code: 'success'};
} catch (e) {
return {
code: 'unexpected_error',
verboseInfo: e.message,
};
}
}
function getShellBinaryAndArgs(
flavor: DebuggerShellFlavor,
): [string, Array<string>] {
switch (flavor) {
case 'prebuilt':
return [require('fb-dotslash'), [DEVTOOLS_BINARY_DOTSLASH_FILE]];
case 'dev':
return [
// NOTE: Internally at Meta, this is aliased to a workspace that is
// API-compatible with the 'electron' package, but contains prebuilt binaries
// that do not need to be downloaded in a postinstall action.
require('electron'),
[require.resolve('../electron')],
];
default:
flavor as empty;
throw new Error(`Unknown flavor: ${flavor}`);
}
}
export {unstable_spawnDebuggerShellWithArgs, unstable_prepareDebuggerShell};
export {unstable_spawnDebuggerShellWithArgs};
@@ -1,98 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {DebuggerShellPreparationResult} from '../';
const {spawn} = require('cross-spawn');
async function spawnAndGetStderr(
command: string,
args: string[],
): Promise<{
code: number,
stderr: string,
}> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
stdio: ['ignore', 'ignore', 'pipe'],
encoding: 'utf8',
windowsHide: true,
});
let stderr = '';
child.stderr.on('data', data => {
stderr += data;
});
child.on('error', error => {
reject(error);
});
child.on('close', (code, signal) => {
resolve({
code,
stderr,
});
});
});
}
async function prepareDebuggerShellFromDotSlashFile(
filePath: string,
): Promise<DebuggerShellPreparationResult> {
const {code, stderr} = await spawnAndGetStderr(require('fb-dotslash'), [
'--',
'fetch',
filePath,
]);
if (code === 0) {
return {code: 'success'};
}
if (
stderr.includes('dotslash error') &&
stderr.includes('no providers succeeded')
) {
if (stderr.includes('failed to verify artifact')) {
return {
code: 'possible_corruption',
humanReadableMessage:
'Failed to verify the latest version of React Native DevTools. ' +
'Using a fallback version instead. ',
verboseInfo: stderr,
};
}
return {
code: 'likely_offline',
humanReadableMessage:
'Failed to download the latest version of React Native DevTools. ' +
'Using a fallback version instead. ' +
'Connect to the internet or check your network settings.',
verboseInfo: stderr,
};
}
if (
stderr.includes('dotslash error') &&
stderr.includes('platform not supported')
) {
return {
code: 'platform_not_supported',
humanReadableMessage:
'The latest version of React Native DevTools is not supported on this platform. ' +
'Using a fallback version instead.',
verboseInfo: stderr,
};
}
return {
code: 'unexpected_error',
humanReadableMessage:
'An unexpected error occured while installing the latest version of React Native DevTools. ' +
'Using a fallback version instead.',
verboseInfo: stderr,
};
}
export {spawnAndGetStderr, prepareDebuggerShellFromDotSlashFile};
-10
View File
@@ -88,16 +88,6 @@ WebSocket handler for registering device connections.
WebSocket handler that proxies CDP messages to/from the corresponding device.
## Experimental features
React Native frameworks may pass an `unstable_experiments` option to `createDevMiddleware` to configure experimental features. Note that these features might not work correctly, and they may change or be removed in the future without notice. Some of the experiment flags available are documented below.
### `unstable_experiments.enableStandaloneFuseboxShell`
When `true`, the debugger frontend will launch in a standalone app shell (provided by the `@react-native/debugger-shell` package) rather than in a browser window. The standalone shell provides an improved experience and will become the default in a future version of React Native.
The shell is powered by a separate binary that is downloaded and cached in the background (immediately after the call to `createDevMiddleware`). If there is a problem downloading or invoking this binary for the first time, the debugger frontend will revert to launching in a browser window until the next time `createDevMiddleware` is called (typically, on the next dev server start).
## Contributing
Changes to this package can be made locally and tested against the `rn-tester` app, per the [Contributing guide](https://reactnative.dev/contributing/overview#contributing-code). During development, this package is automatically run from source with no build step.
+1 -3
View File
@@ -24,7 +24,6 @@
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.82.0-main",
"@react-native/debugger-shell": "0.82.0-main",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
@@ -33,13 +32,12 @@
"nullthrows": "^1.1.1",
"open": "^7.0.3",
"serve-static": "^1.16.2",
"ws": "^7.5.10"
"ws": "^6.2.3"
},
"engines": {
"node": ">= 20.19.4"
},
"devDependencies": {
"@react-native/debugger-shell": "0.82.0-main",
"selfsigned": "^2.4.1",
"undici": "^5.29.0",
"wait-for-expect": "^3.0.2"
@@ -28,9 +28,6 @@ describe('enableStandaloneFuseboxShell experiment', () => {
unstable_showFuseboxShell: () => {
throw new Error('Not implemented');
},
unstable_prepareFuseboxShell: async () => {
return {code: 'not_implemented'};
},
};
const serverRef = withServerForEachTest({
logger: undefined,
@@ -130,7 +127,5 @@ describe('enableStandaloneFuseboxShell experiment', () => {
device.close();
}
});
// TODO(moti): Add tests around unstable_prepareFuseboxShell
});
});
@@ -92,7 +92,6 @@ export default function createDevMiddleware({
const eventReporter = createWrappedEventReporter(
unstable_eventReporter,
logger,
experiments,
);
const inspectorProxy = new InspectorProxy(
@@ -153,7 +152,6 @@ function getExperiments(config: ExperimentsConfig): Experiments {
function createWrappedEventReporter(
reporter: ?EventReporter,
logger: ?Logger,
experiments: Experiments,
): EventReporter {
return {
logEvent(event: ReportableEvent) {
@@ -168,42 +166,10 @@ function createWrappedEventReporter(
logger?.info(
'\u001B[1m\u001B[7m💡 JavaScript logs have moved!\u001B[22m They can now be ' +
'viewed in React Native DevTools. Tip: Type \u001B[1mj\u001B[22m in ' +
'the terminal to open' +
(experiments.enableStandaloneFuseboxShell
? ''
: ' (requires Google Chrome or Microsoft Edge)') +
'.\u001B[27m',
'the terminal to open (requires Google Chrome or Microsoft Edge).' +
'\u001B[27m',
);
break;
case 'fusebox_shell_preparation_attempt':
switch (event.result.code) {
case 'success':
case 'not_implemented':
break;
case 'unexpected_error': {
let message =
event.result.humanReadableMessage ??
'An unknown error occurred while installing React Native DevTools.';
if (event.result.verboseInfo != null) {
message += ` Details:\n\n${event.result.verboseInfo}`;
} else {
message += '.';
}
logger?.error(message);
break;
}
case 'possible_corruption':
case 'platform_not_supported':
case 'likely_offline':
logger?.warn(
event.result.humanReadableMessage ??
`An error of type ${event.result.code} occurred while installing React Native DevTools.`,
);
break;
default:
(event.result.code: empty);
break;
}
}
reporter?.logEvent(event);
+3 -8
View File
@@ -8,17 +8,12 @@
* @format
*/
export type {
BrowserLauncher,
DebuggerShellPreparationResult,
} from './types/BrowserLauncher';
export {default as createDevMiddleware} from './createDevMiddleware';
export type {BrowserLauncher} from './types/BrowserLauncher';
export type {EventReporter, ReportableEvent} from './types/EventReporter';
export type {
CustomMessageHandler,
CustomMessageHandlerConnection,
CreateCustomMessageHandlerFn,
} from './inspector-proxy/CustomMessageHandler';
export type {Logger} from './types/Logger';
export {default as unstable_DefaultBrowserLauncher} from './utils/DefaultBrowserLauncher';
export {default as createDevMiddleware} from './createDevMiddleware';
@@ -970,10 +970,7 @@ export default class Device {
socket: WS,
): void {
const sendSuccessResponse = (scriptSource: string) => {
const result: {
scriptSource: string,
bytecode?: string,
} = {scriptSource};
const result = {scriptSource};
const response: CDPResponse<'Debugger.getScriptSource'> = {
id: req.id,
result,
@@ -10,10 +10,7 @@
import type {InspectorProxyQueries} from '../inspector-proxy/InspectorProxy';
import type {PageDescription} from '../inspector-proxy/types';
import type {
BrowserLauncher,
DebuggerShellPreparationResult,
} from '../types/BrowserLauncher';
import type {BrowserLauncher} from '../types/BrowserLauncher';
import type {EventReporter} from '../types/EventReporter';
import type {Experiments} from '../types/Experiments';
import type {Logger} from '../types/Logger';
@@ -51,19 +48,6 @@ export default function openDebuggerMiddleware({
experiments,
inspectorProxy,
}: Options): NextHandleFunction {
let shellPreparationPromise: Promise<DebuggerShellPreparationResult>;
if (experiments.enableStandaloneFuseboxShell) {
shellPreparationPromise =
browserLauncher?.unstable_prepareFuseboxShell?.() ??
Promise.resolve({code: 'not_implemented'});
shellPreparationPromise = shellPreparationPromise.then(result => {
eventReporter?.logEvent({
type: 'fusebox_shell_preparation_attempt',
result,
});
return result;
});
}
return async (
req: IncomingMessage,
res: ServerResponse,
@@ -83,7 +67,7 @@ export default function openDebuggerMiddleware({
launchId?: string,
telemetryInfo?: string,
target?: string,
panel?: string,
landingView?: string,
...
} = parsedUrl.query;
@@ -168,28 +152,13 @@ export default function openDebuggerMiddleware({
telemetryInfo: query.telemetryInfo,
appId: target.appId,
useFuseboxEntryPoint,
panel: query.panel,
landingView: query.landingView,
},
);
let shouldUseStandaloneFuseboxShell =
useFuseboxEntryPoint && experiments.enableStandaloneFuseboxShell;
if (shouldUseStandaloneFuseboxShell) {
const shellPreparationResult = await shellPreparationPromise;
switch (shellPreparationResult.code) {
case 'success':
case 'not_implemented':
break;
case 'platform_not_supported':
case 'possible_corruption':
case 'likely_offline':
case 'unexpected_error':
shouldUseStandaloneFuseboxShell = false;
break;
default:
(shellPreparationResult.code: empty);
}
}
if (shouldUseStandaloneFuseboxShell) {
if (
useFuseboxEntryPoint &&
experiments.enableStandaloneFuseboxShell
) {
const windowKey = [
serverBaseUrl,
target.webSocketDebuggerUrl,
@@ -8,10 +8,6 @@
* @format
*/
import type {DebuggerShellPreparationResult} from '@react-native/debugger-shell';
export type {DebuggerShellPreparationResult};
/**
* An interface for integrators to provide a custom implementation for
* opening URLs in a web browser.
@@ -46,21 +42,5 @@ export interface BrowserLauncher {
* the host of dev-middleware. Implementations are responsible for rewriting
* this as necessary where the server is remote.
*/
+unstable_showFuseboxShell?: (
url: string,
windowKey: string,
) => Promise<void>;
/**
* Attempt to prepare the debugger shell for use and returns a coded result
* that can be used to advise the user on how to proceed in case of failure.
*
* This function MAY be called multiple times or not at all. Implementers
* SHOULD use the opportunity to prefetch and cache any expensive resources (e.g
* platform-specific binaries needed in order to show the Fusebox shell). After a
* successful call, subsequent calls SHOULD complete quickly. The implementation
* SHOULD NOT return a rejecting promise in any case, and instead SHOULD report
* errors via the returned result object.
*/
+unstable_prepareFuseboxShell?: () => Promise<DebuggerShellPreparationResult>;
unstable_showFuseboxShell?: (url: string, windowKey: string) => Promise<void>;
}
@@ -8,8 +8,6 @@
* @format
*/
import type {DebuggerShellPreparationResult} from './BrowserLauncher';
type SuccessResult<Props: {...} | void = {}> = {
status: 'success',
...Props,
@@ -134,10 +132,6 @@ export type ReportableEvent =
duration: number,
...ConnectionUptime,
...DebuggerSessionIDs,
}
| {
type: 'fusebox_shell_preparation_attempt',
result: DebuggerShellPreparationResult,
};
/**
@@ -26,7 +26,9 @@ export type Experiments = $ReadOnly<{
/**
* Launch the Fusebox frontend in a standalone shell instead of a browser.
* When this is enabled, we will use the optional unstable_showFuseboxShell
* method on the BrowserLauncher, or throw an error if the method is missing.
* method on the framework-provided BrowserLauncher, or throw an error if the
* method is missing. Note that the default BrowserLauncher does *not*
* implement unstable_showFuseboxShell.
*/
enableStandaloneFuseboxShell: boolean,
}>;
@@ -8,12 +8,6 @@
* @format
*/
import type {DebuggerShellPreparationResult} from '../';
const {
unstable_prepareDebuggerShell,
unstable_spawnDebuggerShellWithArgs,
} = require('@react-native/debugger-shell');
const {spawn} = require('child_process');
const ChromeLauncher = require('chrome-launcher');
const {Launcher: EdgeLauncher} = require('chromium-edge-launcher');
@@ -68,25 +62,6 @@ const DefaultBrowserLauncher = {
});
});
},
async unstable_showFuseboxShell(
url: string,
windowKey: string,
): Promise<void> {
return await unstable_spawnDebuggerShellWithArgs(
['--frontendUrl=' + url, '--windowKey=' + windowKey],
{
mode: 'detached',
flavor: process.env.RNDT_DEV === '1' ? 'dev' : 'prebuilt',
},
);
},
async unstable_prepareFuseboxShell(): Promise<DebuggerShellPreparationResult> {
return await unstable_prepareDebuggerShell(
process.env.RNDT_DEV === '1' ? 'dev' : 'prebuilt',
);
},
};
export default DefaultBrowserLauncher;
@@ -24,7 +24,7 @@ export default function getDevToolsFrontendUrl(
/** Whether to use the modern `rn_fusebox.html` entry point. */
useFuseboxEntryPoint?: boolean,
appId?: string,
panel?: string,
landingView?: string,
}>,
): string {
const wsParam = getWsParam({
@@ -55,8 +55,8 @@ export default function getDevToolsFrontendUrl(
if (options?.telemetryInfo != null && options.telemetryInfo !== '') {
searchParams.append('telemetryInfo', options.telemetryInfo);
}
if (options?.panel != null && options.panel !== '') {
searchParams.append('panel', options.panel);
if (options?.landingView != null && options.landingView !== '') {
searchParams.append('landingView', options.landingView);
}
return appUrl + '?' + searchParams.toString();
@@ -28,7 +28,6 @@ import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import com.facebook.react.utils.findPackageJsonFile
import java.io.File
@@ -55,10 +54,6 @@ class ReactPlugin : Plugin<Project> {
project,
)
if (project.rootProject.isHermesV1Enabled) {
rootExtension.hermesV1Enabled.set(true)
}
// App Only Configuration
project.pluginManager.withPlugin("com.android.application") {
// We wire the root extension with the values coming from the app (either user populated or
@@ -71,12 +66,10 @@ class ReactPlugin : Plugin<Project> {
project.afterEvaluate {
val reactNativeDir = extension.reactNativeDir.get().asFile
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
val hermesVersionPropertiesFile =
File(reactNativeDir, "sdks/hermes-engine/version.properties")
val versionAndGroupStrings =
readVersionAndGroupStrings(propertiesFile, hermesVersionPropertiesFile)
val hermesV1Enabled = rootExtension.hermesV1Enabled.get()
configureDependencies(project, versionAndGroupStrings, hermesV1Enabled)
val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
val versionString = versionAndGroupStrings.first
val groupString = versionAndGroupStrings.second
configureDependencies(project, versionString, groupString)
configureRepositories(project)
}
@@ -14,7 +14,6 @@ import com.facebook.react.utils.KotlinStdlibCompatUtils.capitalizeCompat
import com.facebook.react.utils.NdkConfiguratorUtils.configureJsEnginePackagingOptions
import com.facebook.react.utils.NdkConfiguratorUtils.configureNewArchPackagingOptions
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
import com.facebook.react.utils.ProjectUtils.useThirdPartyJSC
import com.facebook.react.utils.detectedCliFile
import com.facebook.react.utils.detectedEntryFile
@@ -49,7 +48,6 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
} else {
isHermesEnabledInProject
}
val isHermesV1Enabled = project.isHermesV1Enabled || rootProject.isHermesV1Enabled
val isDebuggableVariant =
config.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
val useThirdPartyJSC = project.useThirdPartyJSC
@@ -67,27 +65,25 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
if (!isDebuggableVariant) {
val entryFileEnvVariable = System.getenv("ENTRY_FILE")
val bundleTask =
tasks.register("createBundle${targetName}JsAndAssets", BundleHermesCTask::class.java) { task
->
task.root.set(config.root)
task.nodeExecutableAndArgs.set(config.nodeExecutableAndArgs)
task.cliFile.set(cliFile)
task.bundleCommand.set(config.bundleCommand)
task.entryFile.set(detectedEntryFile(config, entryFileEnvVariable))
task.extraPackagerArgs.set(config.extraPackagerArgs)
task.bundleConfig.set(config.bundleConfig)
task.bundleAssetName.set(config.bundleAssetName)
task.jsBundleDir.set(jsBundleDir)
task.resourcesDir.set(resourcesDir)
task.hermesEnabled.set(isHermesEnabledInThisVariant)
task.hermesV1Enabled.set(isHermesV1Enabled)
task.minifyEnabled.set(!isHermesEnabledInThisVariant)
task.devEnabled.set(false)
task.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
task.jsSourceMapsDir.set(jsSourceMapsDir)
task.hermesCommand.set(config.hermesCommand)
task.hermesFlags.set(config.hermesFlags)
task.reactNativeDir.set(config.reactNativeDir)
tasks.register("createBundle${targetName}JsAndAssets", BundleHermesCTask::class.java) {
it.root.set(config.root)
it.nodeExecutableAndArgs.set(config.nodeExecutableAndArgs)
it.cliFile.set(cliFile)
it.bundleCommand.set(config.bundleCommand)
it.entryFile.set(detectedEntryFile(config, entryFileEnvVariable))
it.extraPackagerArgs.set(config.extraPackagerArgs)
it.bundleConfig.set(config.bundleConfig)
it.bundleAssetName.set(config.bundleAssetName)
it.jsBundleDir.set(jsBundleDir)
it.resourcesDir.set(resourcesDir)
it.hermesEnabled.set(isHermesEnabledInThisVariant)
it.minifyEnabled.set(!isHermesEnabledInThisVariant)
it.devEnabled.set(false)
it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
it.jsSourceMapsDir.set(jsSourceMapsDir)
it.hermesCommand.set(config.hermesCommand)
it.hermesFlags.set(config.hermesFlags)
it.reactNativeDir.set(config.reactNativeDir)
}
variant.sources.res?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::resourcesDir)
variant.sources.assets?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::jsBundleDir)
@@ -11,7 +11,6 @@ import javax.inject.Inject
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
/**
* A private extension we set on the rootProject to make easier to share values at execution time
@@ -58,6 +57,4 @@ abstract class PrivateReactExtension @Inject constructor(project: Project) {
val codegenDir: DirectoryProperty =
objects.directoryProperty().convention(root.dir("node_modules/@react-native/codegen"))
val hermesV1Enabled: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
}
@@ -34,15 +34,15 @@ abstract class BundleHermesCTask : DefaultTask() {
@get:InputFiles
val sources: ConfigurableFileTree =
project.fileTree(root) { fileTree ->
fileTree.include("**/*.js")
fileTree.include("**/*.jsx")
fileTree.include("**/*.ts")
fileTree.include("**/*.tsx")
fileTree.exclude("**/android/**/*")
fileTree.exclude("**/ios/**/*")
fileTree.exclude("**/build/**/*")
fileTree.exclude("**/node_modules/**/*")
project.fileTree(root) {
it.include("**/*.js")
it.include("**/*.jsx")
it.include("**/*.ts")
it.include("**/*.tsx")
it.exclude("**/android/**/*")
it.exclude("**/ios/**/*")
it.exclude("**/build/**/*")
it.exclude("**/node_modules/**/*")
}
@get:Input abstract val nodeExecutableAndArgs: ListProperty<String>
@@ -63,8 +63,6 @@ abstract class BundleHermesCTask : DefaultTask() {
@get:Input abstract val hermesEnabled: Property<Boolean>
@get:Input abstract val hermesV1Enabled: Property<Boolean>
@get:Input abstract val devEnabled: Property<Boolean>
@get:Input abstract val extraPackagerArgs: ListProperty<String>
@@ -96,8 +94,7 @@ abstract class BundleHermesCTask : DefaultTask() {
runCommand(bundleCommand)
if (hermesEnabled.get()) {
val detectedHermesCommand =
detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled.get())
val detectedHermesCommand = detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get())
val bytecodeFile = File("${bundleFile}.hbc")
val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
@@ -42,35 +42,35 @@ abstract class PreparePrefabHeadersTask : DefaultTask() {
input.get().forEach { (libraryName, pathToPrefixCouples) ->
val outputFolder: RegularFile = outputDir.file(libraryName).get()
pathToPrefixCouples.forEach { (headerPath, headerPrefix) ->
fs.copy { copySpec ->
copySpec.from(headerPath)
copySpec.include("**/*.h")
copySpec.exclude("**/*.cpp")
copySpec.exclude("**/*.txt")
fs.copy {
it.from(headerPath)
it.include("**/*.h")
it.exclude("**/*.cpp")
it.exclude("**/*.txt")
// We don't want to copy all the boost headers as they are 250Mb+
copySpec.include("boost/config.hpp")
copySpec.include("boost/config/**/*.hpp")
copySpec.include("boost/core/*.hpp")
copySpec.include("boost/detail/workaround.hpp")
copySpec.include("boost/operators.hpp")
copySpec.include("boost/preprocessor/**/*.hpp")
it.include("boost/config.hpp")
it.include("boost/config/**/*.hpp")
it.include("boost/core/*.hpp")
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
copySpec.include("boost/container_hash/**/*.hpp")
copySpec.include("boost/detail/**/*.hpp")
copySpec.include("boost/intrusive/**/*.hpp")
copySpec.include("boost/iterator/**/*.hpp")
copySpec.include("boost/move/**/*.hpp")
copySpec.include("boost/mpl/**/*.hpp")
copySpec.include("boost/mp11/**/*.hpp")
copySpec.include("boost/describe/**/*.hpp")
copySpec.include("boost/type_traits/**/*.hpp")
copySpec.include("boost/utility/**/*.hpp")
copySpec.include("boost/assert.hpp")
copySpec.include("boost/static_assert.hpp")
copySpec.include("boost/cstdint.hpp")
copySpec.include("boost/utility.hpp")
copySpec.include("boost/version.hpp")
copySpec.into(File(outputFolder.asFile, headerPrefix))
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/type_traits/**/*.hpp")
it.include("boost/utility/**/*.hpp")
it.include("boost/assert.hpp")
it.include("boost/static_assert.hpp")
it.include("boost/cstdint.hpp")
it.include("boost/utility.hpp")
it.include("boost/version.hpp")
it.into(File(outputFolder.asFile, headerPrefix))
}
}
}
@@ -7,15 +7,12 @@
package com.facebook.react.utils
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.EXCLUSIVE_ENTEPRISE_REPOSITORY
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_V1_VERSION_NAME
import com.facebook.react.utils.PropertyUtils.INTERNAL_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY
import com.facebook.react.utils.PropertyUtils.INTERNAL_VERSION_NAME
import com.facebook.react.utils.PropertyUtils.SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY
@@ -28,14 +25,6 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository
internal object DependencyUtils {
internal data class Coordinates(
val versionString: String,
val hermesVersionString: String,
val hermesV1VersionString: String,
val reactGroupString: String = DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP,
val hermesGroupString: String = DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP,
)
/**
* This method takes care of configuring the repositories{} block for both the app and all the 3rd
* party libraries which are auto-linked.
@@ -106,20 +95,14 @@ internal object DependencyUtils {
* This method takes care of configuring the resolution strategy for both the app and all the 3rd
* party libraries which are auto-linked. Specifically it takes care of:
* - Forcing the react-android/hermes-android version to the one specified in the package.json
* - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`
* - Selecting between the classic Hermes and Hermes V1
* - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`.
*/
fun configureDependencies(
project: Project,
coordinates: Coordinates,
hermesV1Enabled: Boolean = false,
versionString: String,
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
) {
if (
coordinates.versionString.isBlank() ||
(!hermesV1Enabled && coordinates.hermesVersionString.isBlank()) ||
(hermesV1Enabled && coordinates.hermesV1VersionString.isBlank())
)
return
if (versionString.isBlank()) return
project.rootProject.allprojects { eachProject ->
eachProject.configurations.all { configuration ->
// Here we set a dependencySubstitution for both react-native and hermes-engine as those
@@ -127,67 +110,53 @@ internal object DependencyUtils {
// This allows users to import libraries that are still using
// implementation("com.facebook.react:react-native:+") and resolve the right dependency.
configuration.resolutionStrategy.dependencySubstitution {
getDependencySubstitutions(coordinates, hermesV1Enabled).forEach { (module, dest, reason)
->
getDependencySubstitutions(versionString, groupString).forEach { (module, dest, reason) ->
it.substitute(it.module(module)).using(it.module(dest)).because(reason)
}
}
configuration.resolutionStrategy.force(
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
"${groupString}:react-android:${versionString}",
)
if (!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY) as? String).toBoolean()) {
// Contributors only: The hermes-engine version is forced only if the user has
// not opted into using nightlies for local development.
configuration.resolutionStrategy.force(
// TODO: T237406039 update coordinates
if (hermesV1Enabled)
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
else
"${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
)
configuration.resolutionStrategy.force("${groupString}:hermes-android:${versionString}")
}
}
}
}
internal fun getDependencySubstitutions(
coordinates: Coordinates,
hermesV1Enabled: Boolean = false,
versionString: String,
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
): List<Triple<String, String, String>> {
// TODO: T231755027 update coordinates and versioning
val dependencySubstitution = mutableListOf<Triple<String, String, String>>()
// TODO: T237406039 update coordinates
val hermesVersionString =
if (hermesV1Enabled)
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
else "${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
dependencySubstitution.add(
Triple(
"com.facebook.react:react-native",
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
"${groupString}:react-android:${versionString}",
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.",
)
)
dependencySubstitution.add(
Triple(
"com.facebook.react:hermes-engine",
hermesVersionString,
"${groupString}:hermes-android:${versionString}",
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.",
)
)
if (coordinates.reactGroupString != DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP) {
if (groupString != DEFAULT_INTERNAL_PUBLISHING_GROUP) {
dependencySubstitution.add(
Triple(
"com.facebook.react:react-android",
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
"${groupString}:react-android:${versionString}",
"The react-android dependency was modified to use the correct Maven group.",
)
)
// TODO: T237406039 update coordinates
dependencySubstitution.add(
Triple(
"com.facebook.react:hermes-android",
hermesVersionString,
"${groupString}:hermes-android:${versionString}",
"The hermes-android dependency was modified to use the correct Maven group.",
)
)
@@ -195,7 +164,7 @@ internal object DependencyUtils {
return dependencySubstitution
}
fun readVersionAndGroupStrings(propertiesFile: File, hermesVersionFile: File): Coordinates {
fun readVersionAndGroupStrings(propertiesFile: File): Pair<String, String> {
val reactAndroidProperties = Properties()
propertiesFile.inputStream().use { reactAndroidProperties.load(it) }
val versionStringFromFile = (reactAndroidProperties[INTERNAL_VERSION_NAME] as? String).orEmpty()
@@ -207,27 +176,10 @@ internal object DependencyUtils {
versionStringFromFile
}
// Returns Maven group for repos using different group for Maven artifacts
val reactGroupString =
reactAndroidProperties[INTERNAL_REACT_PUBLISHING_GROUP] as? String
?: DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP
val hermesGroupString =
reactAndroidProperties[INTERNAL_HERMES_PUBLISHING_GROUP] as? String
?: DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
// TODO: T237406039 read both versions from the same file
val hermesVersionProperties = Properties()
hermesVersionFile.inputStream().use { hermesVersionProperties.load(it) }
val hermesVersion = versionString
val hermesV1Version =
(hermesVersionProperties[INTERNAL_HERMES_V1_VERSION_NAME] as? String).orEmpty()
return Coordinates(
versionString,
hermesVersion,
hermesV1Version,
reactGroupString,
hermesGroupString,
)
val groupString =
reactAndroidProperties[INTERNAL_PUBLISHING_GROUP] as? String
?: DEFAULT_INTERNAL_PUBLISHING_GROUP
return Pair(versionString, groupString)
}
fun Project.mavenRepoFromUrl(
@@ -122,16 +122,11 @@ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): F
* used if the user is building Hermes from source.
* 3. The file located in `node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc` where `%OS-BIN%`
* is substituted with the correct OS arch. This will be used if the user is using a precompiled
* hermes-engine package. Or, if the user has opted in to use Hermes V1, the used file will be
* located in `node_modules/hermes-compiler/%OS-BIN%/hermesc` where `%OS-BIN%` is substituted
* with the correct OS arch.
* hermes-engine package.
* 4. Fails otherwise
*/
internal fun detectOSAwareHermesCommand(
projectRoot: File,
hermesCommand: String,
hermesV1Enabled: Boolean = false,
): String { // 1. If the project specifies a Hermes command, don't second guess it.
internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String): String {
// 1. If the project specifies a Hermes command, don't second guess it.
if (hermesCommand.isNotBlank()) {
val osSpecificHermesCommand =
if ("%OS-BIN%" in hermesCommand) {
@@ -151,13 +146,9 @@ internal fun detectOSAwareHermesCommand(
return builtHermesc.cliPath(projectRoot)
}
// 3. If Hermes V1 is enabled, use hermes-compiler from npm, otherwise, if the
// react-native contains a pre-built hermesc, use it.
// TODO: T237406039 use hermes-compiler from npm for both
val hermesCPath = if (hermesV1Enabled) HERMES_COMPILER_NPM_DIR else HERMESC_IN_REACT_NATIVE_DIR
// 3. If the react-native contains a pre-built hermesc, use it.
val prebuiltHermesPath =
hermesCPath
.plus(getHermesCBin())
HERMESC_IN_REACT_NATIVE_DIR.plus(getHermesCBin())
.replace("%OS-BIN%", getHermesOSBin())
// Execution on Windows fails with / as separator
.replace('/', File.separatorChar)
@@ -242,7 +233,6 @@ internal fun readPackageJsonFile(
return packageJson?.let { JsonUtils.fromPackageJson(it) }
}
private const val HERMES_COMPILER_NPM_DIR = "node_modules/hermes-compiler/%OS-BIN%/"
private const val HERMESC_IN_REACT_NATIVE_DIR = "node_modules/react-native/sdks/hermesc/%OS-BIN%/"
private const val HERMESC_BUILT_FROM_SOURCE_DIR =
"node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/"
@@ -13,17 +13,14 @@ import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
import com.facebook.react.utils.PropertyUtils.EDGE_TO_EDGE_ENABLED
import com.facebook.react.utils.PropertyUtils.HERMES_ENABLED
import com.facebook.react.utils.PropertyUtils.HERMES_V1_ENABLED
import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES
import com.facebook.react.utils.PropertyUtils.SCOPED_EDGE_TO_EDGE_ENABLED
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_V1_ENABLED
import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES
import com.facebook.react.utils.PropertyUtils.SCOPED_USE_THIRD_PARTY_JSC
import com.facebook.react.utils.PropertyUtils.USE_THIRD_PARTY_JSC
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.jetbrains.kotlin.gradle.plugin.extraProperties
internal object ProjectUtils {
@@ -71,17 +68,6 @@ internal object ProjectUtils {
(project.hasProperty(SCOPED_USE_THIRD_PARTY_JSC) &&
project.property(SCOPED_USE_THIRD_PARTY_JSC).toString().toBoolean())
internal val Project.isHermesV1Enabled: Boolean
get() =
(project.hasProperty(HERMES_V1_ENABLED) &&
project.property(HERMES_V1_ENABLED).toString().toBoolean()) ||
(project.hasProperty(SCOPED_HERMES_V1_ENABLED) &&
project.property(SCOPED_HERMES_V1_ENABLED).toString().toBoolean()) ||
(project.extraProperties.has(HERMES_V1_ENABLED) &&
project.extraProperties.get(HERMES_V1_ENABLED).toString().toBoolean()) ||
(project.extraProperties.has(SCOPED_HERMES_V1_ENABLED) &&
project.extraProperties.get(SCOPED_HERMES_V1_ENABLED).toString().toBoolean())
internal fun Project.needsCodegenFromPackageJson(rootProperty: DirectoryProperty): Boolean {
val parsedPackageJson = readPackageJsonFile(this, rootProperty)
return needsCodegenFromPackageJson(parsedPackageJson)
@@ -18,10 +18,6 @@ object PropertyUtils {
const val HERMES_ENABLED = "hermesEnabled"
const val SCOPED_HERMES_ENABLED = "react.hermesEnabled"
/** Public property that toggles Hermes V1 */
const val HERMES_V1_ENABLED = "hermesV1Enabled"
const val SCOPED_HERMES_V1_ENABLED = "react.hermesV1Enabled"
/** Public property that toggles edge-to-edge */
const val EDGE_TO_EDGE_ENABLED = "edgeToEdgeEnabled"
const val SCOPED_EDGE_TO_EDGE_ENABLED = "react.edgeToEdgeEnabled"
@@ -72,17 +68,9 @@ object PropertyUtils {
const val INTERNAL_USE_HERMES_NIGHTLY = "react.internal.useHermesNightly"
/** Internal property used to override the publishing group for the React Native artifacts. */
const val INTERNAL_REACT_PUBLISHING_GROUP = "react.internal.publishingGroup"
const val INTERNAL_HERMES_PUBLISHING_GROUP = "react.internal.hermesPublishingGroup"
const val DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP = "com.facebook.react"
const val DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP = "com.facebook.hermes"
const val INTERNAL_PUBLISHING_GROUP = "react.internal.publishingGroup"
const val DEFAULT_INTERNAL_PUBLISHING_GROUP = "com.facebook.react"
/** Internal property used to control the version name of React Native */
const val INTERNAL_VERSION_NAME = "VERSION_NAME"
/**
* Internal property, shared with iOS, used to control the version name of Hermes Engine. This is
* stored in sdks/hermes-engine/version.properties
*/
const val INTERNAL_HERMES_V1_VERSION_NAME = "HERMES_V1_VERSION_NAME"
}
@@ -34,9 +34,9 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
val inputFile = tempFolder.newFile("config.json")
val task =
createTestTask<GenerateAutolinkingNewArchitecturesFileTask> { task ->
task.generatedOutputDirectory.set(outputFolder)
task.autolinkInputFile.set(inputFile)
createTestTask<GenerateAutolinkingNewArchitecturesFileTask> {
it.generatedOutputDirectory.set(outputFolder)
it.autolinkInputFile.set(inputFile)
}
assertThat(task.generatedOutputDirectory.get().asFile).isEqualTo(outputFolder)
@@ -198,10 +198,10 @@ class GenerateCodegenArtifactsTaskTest {
@Test
fun resolveTaskParameters_withMissingPackageJson_usesGradleOne() {
val task =
createTestTask<GenerateCodegenArtifactsTask> { task ->
task.packageJsonFile.set(File(tempFolder.root, "package.json"))
task.codegenJavaPackageName.set("com.example.test")
task.libraryName.set("a-library-name-from-gradle")
createTestTask<GenerateCodegenArtifactsTask> {
it.packageJsonFile.set(File(tempFolder.root, "package.json"))
it.codegenJavaPackageName.set("com.example.test")
it.libraryName.set("a-library-name-from-gradle")
}
val (libraryName, javaPackageName) = task.resolveTaskParameters()
@@ -33,9 +33,9 @@ class GeneratePackageListTaskTest {
val inputFile = tempFolder.newFile("config.json")
val task =
createTestTask<GeneratePackageListTask> { testTask ->
testTask.generatedOutputDirectory.set(outputFolder)
testTask.autolinkInputFile.set(inputFile)
createTestTask<GeneratePackageListTask> {
it.generatedOutputDirectory.set(outputFolder)
it.autolinkInputFile.set(inputFile)
}
assertThat(task.inputs.files.singleFile).isEqualTo(inputFile)
@@ -58,11 +58,11 @@ class PrepareBoostTaskTest {
val boostThirdPartyJniPath = tempFolder.newFolder("boostpath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareBoostTask> { task ->
task.boostPath.setFrom(boostpath)
task.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
task.boostVersion.set("1.0.0")
task.outputDir.set(output)
createTestTask<PrepareBoostTask> {
it.boostPath.setFrom(boostpath)
it.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
it.boostVersion.set("1.0.0")
it.outputDir.set(output)
}
File(boostpath, "asm/asm.S").apply {
parentFile.mkdirs()
@@ -79,11 +79,11 @@ class PrepareBoostTaskTest {
val boostThirdPartyJniPath = tempFolder.newFolder("boostpath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareBoostTask> { task ->
task.boostPath.setFrom(boostpath)
task.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
task.boostVersion.set("1.0.0")
task.outputDir.set(output)
createTestTask<PrepareBoostTask> {
it.boostPath.setFrom(boostpath)
it.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
it.boostVersion.set("1.0.0")
it.outputDir.set(output)
}
File(boostpath, "boost_1.0.0/boost/config.hpp").apply {
parentFile.mkdirs()
@@ -100,11 +100,11 @@ class PrepareBoostTaskTest {
val boostThirdPartyJniPath = tempFolder.newFolder("boostpath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareBoostTask> { task ->
task.boostPath.setFrom(boostpath)
task.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
task.boostVersion.set("1.0.0")
task.outputDir.set(output)
createTestTask<PrepareBoostTask> {
it.boostPath.setFrom(boostpath)
it.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
it.boostVersion.set("1.0.0")
it.outputDir.set(output)
}
File(boostpath, "boost/boost/config.hpp").apply {
parentFile.mkdirs()
@@ -82,11 +82,11 @@ class PrepareGflagsTaskTest {
val gflagsThirdPartyPath = tempFolder.newFolder("gflagspath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareGflagsTask> { taskConfig ->
taskConfig.gflagsPath.setFrom(gflagspath)
taskConfig.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
taskConfig.gflagsVersion.set("1.0.0")
taskConfig.outputDir.set(output)
createTestTask<PrepareGflagsTask> {
it.gflagsPath.setFrom(gflagspath)
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
it.gflagsVersion.set("1.0.0")
it.outputDir.set(output)
}
File(gflagspath, "gflags-1.0.0/src/gflags_declare.h.in").apply {
parentFile.mkdirs()
@@ -98,11 +98,11 @@ class PrepareGlogTaskTest {
val glogThirdPartyJniPath = tempFolder.newFolder("glogpath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareGlogTask> { task ->
task.glogPath.setFrom(glogpath)
task.glogThirdPartyJniPath.set(glogThirdPartyJniPath)
task.glogVersion.set("1.0.0")
task.outputDir.set(output)
createTestTask<PrepareGlogTask> {
it.glogPath.setFrom(glogpath)
it.glogThirdPartyJniPath.set(glogThirdPartyJniPath)
it.glogVersion.set("1.0.0")
it.outputDir.set(output)
}
File(glogpath, "glog-1.0.0/src/glog.h.in").apply {
parentFile.mkdirs()
@@ -122,11 +122,11 @@ class PrepareGlogTaskTest {
val glogThirdPartyJniPath = tempFolder.newFolder("glogpath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareGlogTask> { task ->
task.glogPath.setFrom(glogpath)
task.glogThirdPartyJniPath.set(glogThirdPartyJniPath)
task.glogVersion.set("1.0.0")
task.outputDir.set(output)
createTestTask<PrepareGlogTask> {
it.glogPath.setFrom(glogpath)
it.glogThirdPartyJniPath.set(glogThirdPartyJniPath)
it.glogVersion.set("1.0.0")
it.outputDir.set(output)
}
File(glogpath, "glog-1.0.0/src/logging.h.in").apply {
parentFile.mkdirs()
@@ -186,9 +186,9 @@ class PreparePrefabHeadersTaskTest {
val project = createProject(projectDir = tempFolder.root)
val task =
createTestTask<PreparePrefabHeadersTask>(project = project) { task ->
task.outputDir.set(outputDir)
task.input.set(listOf(PrefabPreprocessingEntry("sample_library", "boost/" to "")))
createTestTask<PreparePrefabHeadersTask>(project = project) {
it.outputDir.set(outputDir)
it.input.set(listOf(PrefabPreprocessingEntry("sample_library", "boost/" to "")))
}
task.taskAction()
@@ -59,8 +59,8 @@ internal fun createZip(dest: File, paths: List<String>) {
val uri = URI.create("jar:file:$dest")
FileSystems.newFileSystem(uri, env).use { zipfs ->
paths.forEach { path ->
val zipEntryPath = zipfs.getPath(path)
paths.forEach {
val zipEntryPath = zipfs.getPath(it)
val zipEntryFolder = zipEntryPath.subpath(0, zipEntryPath.nameCount - 1)
Files.createDirectories(zipEntryFolder)
Files.createFile(zipEntryPath)
@@ -275,160 +275,71 @@ class DependencyUtilsTest {
fun configureDependencies_withEmptyVersion_doesNothing() {
val project = createProject()
configureDependencies(project, DependencyUtils.Coordinates("", "", ""))
configureDependencies(project, "")
assertThat(project.configurations.first().resolutionStrategy.forcedModules.isEmpty()).isTrue()
}
@Test
fun configureDependencies_withVersionString_appliesResolutionStrategy_withClassicHermes() {
fun configureDependencies_withVersionString_appliesResolutionStrategy() {
val project = createProject()
configureDependencies(project, DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"))
configureDependencies(project, "1.2.3")
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
.isTrue()
assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
.isTrue()
}
@Test
fun configureDependencies_withVersionString_appliesResolutionStrategy_withHermesV1() {
val project = createProject()
configureDependencies(
project,
DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"),
hermesV1Enabled = true,
)
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
.isTrue()
assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
.isTrue()
}
@Test
fun configureDependencies_withVersionString_appliesOnAllProjects_withClassicHermes() {
fun configureDependencies_withVersionString_appliesOnAllProjects() {
val rootProject = ProjectBuilder.builder().build()
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
appProject.plugins.apply("com.android.application")
libProject.plugins.apply("com.android.library")
configureDependencies(appProject, DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"))
configureDependencies(appProject, "1.2.3")
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
.isTrue()
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
.isTrue()
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
.isTrue()
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
.isTrue()
}
@Test
fun configureDependencies_withVersionString_appliesOnAllProjects_withHermesV1() {
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects() {
val rootProject = ProjectBuilder.builder().build()
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
appProject.plugins.apply("com.android.application")
libProject.plugins.apply("com.android.library")
configureDependencies(
appProject,
DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"),
hermesV1Enabled = true,
)
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
.isTrue()
assertThat(appForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
.isTrue()
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
.isTrue()
assertThat(libForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
.isTrue()
}
@Test
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects_withClassicHermes() {
val rootProject = ProjectBuilder.builder().build()
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
appProject.plugins.apply("com.android.application")
libProject.plugins.apply("com.android.library")
configureDependencies(
appProject,
DependencyUtils.Coordinates(
"1.2.3",
"4.5.6",
"7.8.9",
"io.github.test",
"io.github.test.hermes",
),
)
configureDependencies(appProject, "1.2.3", "io.github.test")
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
.isTrue()
assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
.isTrue()
assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
.isTrue()
assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
.isTrue()
}
@Test
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects_withHermesV1() {
val rootProject = ProjectBuilder.builder().build()
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
appProject.plugins.apply("com.android.application")
libProject.plugins.apply("com.android.library")
configureDependencies(
appProject,
DependencyUtils.Coordinates(
"1.2.3",
"4.5.6",
"7.8.9",
"io.github.test",
"io.github.test.hermes",
),
hermesV1Enabled = true,
)
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
.isTrue()
assertThat(
appForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:7.8.9" }
)
.isTrue()
assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
.isTrue()
assertThat(
libForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:7.8.9" }
)
.isTrue()
}
@Test
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly_withClassicHermes() {
val dependencySubstitutions =
getDependencySubstitutions(DependencyUtils.Coordinates("0.42.0", "0.42.0", "0.43.0"))
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly() {
val dependencySubstitutions = getDependencySubstitutions("0.42.0")
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
assertThat("com.facebook.react:react-android:0.42.0")
@@ -447,41 +358,8 @@ class DependencyUtilsTest {
}
@Test
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly_withHermesV1() {
val dependencySubstitutions =
getDependencySubstitutions(
DependencyUtils.Coordinates("0.42.0", "0.42.0", "0.43.0"),
hermesV1Enabled = true,
)
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
assertThat("com.facebook.react:react-android:0.42.0")
.isEqualTo(dependencySubstitutions[0].second)
assertThat(
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."
)
.isEqualTo(dependencySubstitutions[0].third)
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
assertThat("com.facebook.hermes:hermes-android:0.43.0")
.isEqualTo(dependencySubstitutions[1].second)
assertThat(
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
)
.isEqualTo(dependencySubstitutions[1].third)
}
@Test
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly_withClassicHermes() {
val dependencySubstitutions =
getDependencySubstitutions(
DependencyUtils.Coordinates(
"0.42.0",
"0.42.0",
"0.43.0",
"io.github.test",
"io.github.test.hermes",
)
)
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly() {
val dependencySubstitutions = getDependencySubstitutions("0.42.0", "io.github.test")
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[0].second)
@@ -505,44 +383,6 @@ class DependencyUtilsTest {
.isEqualTo(dependencySubstitutions[3].third)
}
@Test
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly_withHermesV1() {
val dependencySubstitutions =
getDependencySubstitutions(
DependencyUtils.Coordinates(
"0.42.0",
"0.42.0",
"0.43.0",
"io.github.test",
"io.github.test.hermes",
),
hermesV1Enabled = true,
)
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[0].second)
assertThat(
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."
)
.isEqualTo(dependencySubstitutions[0].third)
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
assertThat("io.github.test.hermes:hermes-android:0.43.0")
.isEqualTo(dependencySubstitutions[1].second)
assertThat(
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
)
.isEqualTo(dependencySubstitutions[1].third)
assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[2].first)
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[2].second)
assertThat("The react-android dependency was modified to use the correct Maven group.")
.isEqualTo(dependencySubstitutions[2].third)
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[3].first)
assertThat("io.github.test.hermes:hermes-android:0.43.0")
.isEqualTo(dependencySubstitutions[3].second)
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
.isEqualTo(dependencySubstitutions[3].third)
}
@Test
fun readVersionString_withCorrectVersionString_returnsIt() {
val propertiesFile =
@@ -556,25 +396,9 @@ class DependencyUtilsTest {
)
}
val hermesVersionFile =
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_V1_VERSION_NAME=1000.0.0
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
val versionString = strings.versionString
val hermesVersionString = strings.hermesVersionString
val hermesV1VersionString = strings.hermesV1VersionString
val versionString = readVersionAndGroupStrings(propertiesFile).first
assertThat(versionString).isEqualTo("1000.0.0")
assertThat(hermesVersionString).isEqualTo("1000.0.0")
assertThat(hermesV1VersionString).isEqualTo("1000.0.0")
}
@Test
@@ -584,33 +408,15 @@ class DependencyUtilsTest {
writeText(
"""
VERSION_NAME=0.0.0-20221101-2019-cfe811ab1
HERMES_VERSION_NAME=0.12.0-commitly-20221101-2019-cfe811ab1
HERMES_V1_VERSION_NAME=250829098.0.0-stable
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val hermesVersionFile =
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_V1_VERSION_NAME=250829098.0.0-stable
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
val versionString = strings.versionString
val hermesVersionString = strings.hermesVersionString
val hermesV1VersionString = strings.hermesV1VersionString
val versionString = readVersionAndGroupStrings(propertiesFile).first
assertThat(versionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
assertThat(hermesVersionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
assertThat(hermesV1VersionString).isEqualTo("250829098.0.0-stable")
}
@Test
@@ -625,23 +431,8 @@ class DependencyUtilsTest {
)
}
val hermesVersionFile =
tempFolder.newFile("version.properties").apply {
writeText(
"""
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
val versionString = strings.versionString
val hermesVersionString = strings.hermesVersionString
val hermesV1VersionString = strings.hermesV1VersionString
val versionString = readVersionAndGroupStrings(propertiesFile).first
assertThat(versionString).isEqualTo("")
assertThat(hermesVersionString).isEqualTo("")
assertThat(hermesV1VersionString).isEqualTo("")
}
@Test
@@ -657,24 +448,8 @@ class DependencyUtilsTest {
)
}
val hermesVersionFile =
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_V1_VERSION_NAME=
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
val versionString = strings.versionString
val hermesVersionString = strings.hermesVersionString
val hermesV1VersionString = strings.hermesV1VersionString
val versionString = readVersionAndGroupStrings(propertiesFile).first
assertThat(versionString).isEqualTo("")
assertThat(hermesVersionString).isEqualTo("")
assertThat(hermesV1VersionString).isEqualTo("")
}
@Test
@@ -684,30 +459,15 @@ class DependencyUtilsTest {
writeText(
"""
react.internal.publishingGroup=io.github.test
react.internal.hermesPublishingGroup=io.github.test
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val hermesVersionFile =
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_V1_VERSION_NAME=
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val groupString = readVersionAndGroupStrings(propertiesFile).second
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
val reactGroupString = strings.reactGroupString
val hermesGroupString = strings.hermesGroupString
assertThat(reactGroupString).isEqualTo("io.github.test")
assertThat(hermesGroupString).isEqualTo("io.github.test")
assertThat(groupString).isEqualTo("io.github.test")
}
@Test
@@ -722,23 +482,9 @@ class DependencyUtilsTest {
)
}
val hermesVersionFile =
tempFolder.newFile("version.properties").apply {
writeText(
"""
HERMES_V1_VERSION_NAME=
ANOTHER_PROPERTY=true
"""
.trimIndent()
)
}
val groupString = readVersionAndGroupStrings(propertiesFile).second
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
val reactGroupString = strings.reactGroupString
val hermesGroupString = strings.hermesGroupString
assertThat(reactGroupString).isEqualTo("com.facebook.react")
assertThat(hermesGroupString).isEqualTo("com.facebook.hermes")
assertThat(groupString).isEqualTo("com.facebook.react")
}
@Test
@@ -162,16 +162,6 @@ class PathUtilsTest {
assertThat(detectOSAwareHermesCommand(tempFolder.root, "")).isEqualTo(expected.toString())
}
@Test
@WithOs(OS.MAC)
fun detectOSAwareHermesCommand_withHermesV1Enabled() {
tempFolder.newFolder("node_modules/hermes-compiler/osx-bin/")
val expected = tempFolder.newFile("node_modules/hermes-compiler/osx-bin/hermesc")
assertThat(detectOSAwareHermesCommand(tempFolder.root, "", hermesV1Enabled = true))
.isEqualTo(expected.toString())
}
@Test(expected = IllegalStateException::class)
@WithOs(OS.MAC)
fun detectOSAwareHermesCommand_failsIfNotFound() {
@@ -14,7 +14,6 @@ import com.facebook.react.tests.createProject
import com.facebook.react.utils.ProjectUtils.getReactNativeArchitectures
import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import java.io.File
@@ -116,32 +115,6 @@ class ProjectUtilsTest {
assertThat(project.isEdgeToEdgeEnabled).isFalse()
}
@Test
fun isHermesV1Enabled_returnsFalseByDefault() {
assertThat(createProject().isHermesV1Enabled).isFalse()
}
@Test
fun isHermesV1Enabled_withDisabledViaProperty_returnsFalse() {
val project = createProject()
project.extensions.extraProperties.set("hermesV1Enabled", "false")
assertThat(project.isHermesV1Enabled).isFalse()
}
@Test
fun isHermesV1Enabled_withEnabledViaProperty_returnsTrue() {
val project = createProject()
project.extensions.extraProperties.set("hermesV1Enabled", "true")
assertThat(project.isHermesV1Enabled).isTrue()
}
@Test
fun isHermesV1Enabled_withInvalidViaProperty_returnsFalse() {
val project = createProject()
project.extensions.extraProperties.set("hermesV1Enabled", "¯\\_(ツ)_/¯")
assertThat(project.isHermesV1Enabled).isFalse()
}
@Test
fun needsCodegenFromPackageJson_withCodegenConfigInPackageJson_returnsTrue() {
val project = createProject()
@@ -25,14 +25,14 @@ class OsRule : TestRule {
override fun evaluate() {
val annotation = description.annotations.filterIsInstance<WithOs>().firstOrNull()
annotation?.os?.propertyName?.let { osName ->
annotation?.os?.propertyName?.let {
retainOs = System.getProperty(OS_NAME_KEY)
System.setProperty(OS_NAME_KEY, osName)
System.setProperty(OS_NAME_KEY, it)
}
annotation?.arch?.let { arch ->
if (arch.isNotBlank()) {
annotation?.arch?.let {
if (it.isNotBlank()) {
retainArch = System.getProperty(OS_ARCH_KEY)
System.setProperty(OS_ARCH_KEY, arch)
System.setProperty(OS_ARCH_KEY, it)
}
}
try {
@@ -47,7 +47,7 @@ it('refuses non-spec compliant colors', () => {
expect(normalizeColor('rgb (0, 1, 2)')).toBe(null);
expect(normalizeColor('rgba(0 0 0 0.0)')).toBe(null);
expect(normalizeColor('hsv(0, 1, 2)')).toBe(null);
// $FlowExpectedError[incompatible-type] - Intentionally malformed argument.
// $FlowExpectedError - Intentionally malformed argument.
expect(normalizeColor({r: 10, g: 10, b: 10})).toBe(null);
expect(normalizeColor('hsl(1%, 2, 3)')).toBe(null);
expect(normalizeColor('rgb(1%, 2%, 3%)')).toBe(null);
+1 -1
View File
@@ -218,7 +218,7 @@ const transform /*: BabelTransformer['transform'] */ = ({
// The result from `transformFromAstSync` can be null (if the file is ignored)
if (!result) {
/* $FlowFixMe[incompatible-type] BabelTransformer specifies that the `ast` can never be null but
/* $FlowFixMe BabelTransformer specifies that the `ast` can never be null but
* the function returns here. Discovered when typing `BabelNode`. */
return {ast: null};
}
@@ -350,8 +350,6 @@ function setDefaultValue(
common.default = ((defaultValue ? defaultValue : 0): number);
break;
case 'FloatTypeAnnotation':
/* $FlowFixMe[invalid-compare] Error discovered during Constant Condition
* roll out. See https://fburl.com/workplace/5whu3i34. */
common.default = ((defaultValue === null
? null
: defaultValue
@@ -359,8 +357,6 @@ function setDefaultValue(
: 0): number | null);
break;
case 'BooleanTypeAnnotation':
/* $FlowFixMe[invalid-compare] Error discovered during Constant Condition
* roll out. See https://fburl.com/workplace/5whu3i34. */
common.default = defaultValue === null ? null : !!defaultValue;
break;
case 'StringTypeAnnotation':
@@ -275,7 +275,7 @@ export function formatNativeSpecErrorStore(
export function formatDiffSet(summary: DiffSummary): FormattedDiffSummary {
const summaryStatus = summary.status;
if (summaryStatus === 'ok' || summaryStatus === 'patchable') {
// $FlowFixMe[incompatible-type] I don't think we can ever get in this branch
// $FlowFixMe I don't think we can ever get in this branch
return summary;
}
const hasteModules = Object.keys(summary.incompatibilityReport);
@@ -77,8 +77,6 @@ const ActionSheetIOS = {
callback: (buttonIndex: number) => void,
) {
invariant(
/* $FlowFixMe[invalid-compare] Error discovered during Constant Condition
* roll out. See https://fburl.com/workplace/5whu3i34. */
typeof options === 'object' && options !== null,
'Options must be a valid object',
);
@@ -164,8 +162,6 @@ const ActionSheetIOS = {
successCallback: Function | ((success: boolean, method: ?string) => void),
) {
invariant(
/* $FlowFixMe[invalid-compare] Error discovered during Constant Condition
* roll out. See https://fburl.com/workplace/5whu3i34. */
typeof options === 'object' && options !== null,
'Options must be a valid object',
);
@@ -10,7 +10,6 @@
import AnimatedImplementation from './AnimatedImplementation';
export type {CompositeAnimation} from './AnimatedImplementation';
export type {DecayAnimationConfig} from './animations/DecayAnimation';
export type {SpringAnimationConfig} from './animations/SpringAnimation';
export type {TimingAnimationConfig} from './animations/TimingAnimation';
@@ -54,8 +54,7 @@ const AnimatedScrollView: AnimatedComponentType<
props.style != null
) {
return (
// $FlowFixMe[incompatible-type] - It should return an Animated ScrollView but it returns a ScrollView with Animated props applied.
// $FlowFixMe[incompatible-variance]
// $FlowFixMe - It should return an Animated ScrollView but it returns a ScrollView with Animated props applied.
<AnimatedScrollViewWithInvertedRefreshControl
scrollEventThrottle={0.0001}
{...props}
@@ -14,7 +14,7 @@ import SectionList, {type SectionListProps} from '../../Lists/SectionList';
import createAnimatedComponent from '../createAnimatedComponent';
import * as React from 'react';
// $FlowFixMe[incompatible-type]
// $FlowFixMe
export default (createAnimatedComponent(SectionList): component<
// $FlowExpectedError[unclear-type]
ItemT = any,
@@ -17,7 +17,6 @@ type ValueListenerCallback = (state: {value: number, ...}) => mixed;
export type AnimatedNodeConfig = $ReadOnly<{
debugID?: string,
unstable_disableBatchingForNativeCreate?: boolean,
}>;
let _uniqueId = 1;
@@ -43,8 +42,6 @@ export default class AnimatedNode {
if (__DEV__) {
this.__debugID = config?.debugID;
}
this.__disableBatchingForNativeCreate =
config?.unstable_disableBatchingForNativeCreate;
}
__attach(): void {}
@@ -68,7 +65,6 @@ export default class AnimatedNode {
/* Methods and props used by native Animated impl */
__isNative: boolean = false;
__nativeTag: ?number = undefined;
__disableBatchingForNativeCreate: ?boolean = undefined;
__makeNative(platformConfig: ?PlatformConfig): void {
// Subclasses are expected to set `__isNative` to true before this.
@@ -146,9 +142,6 @@ export default class AnimatedNode {
if (this._platformConfig) {
config.platformConfig = this._platformConfig;
}
if (this.__disableBatchingForNativeCreate) {
config.disableBatchingForNativeCreate = true;
}
NativeAnimatedHelper.API.createAnimatedNode(nativeTag, config);
}
return nativeTag;
@@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTBridgeDelegate.h>
#import <React/RCTConvert.h>
#import <UIKit/UIKit.h>
#import "RCTDefaultReactNativeFactoryDelegate.h"
#import "RCTReactNativeFactory.h"
@@ -66,8 +68,7 @@ __attribute__((deprecated(
/// The window object, used to render the UViewControllers
@property (nonatomic, strong, nonnull) UIWindow *window;
@property (nonatomic, nullable) RCTBridge *bridge
__attribute__((deprecated("The bridge is deprecated and will be removed when removing the legacy architecture.")));
@property (nonatomic, nullable) RCTBridge *bridge;
@property (nonatomic, strong, nullable) NSString *moduleName;
@property (nonatomic, strong, nullable) NSDictionary *initialProps;
@property (nonatomic, strong) RCTReactNativeFactory *reactNativeFactory;
@@ -75,9 +76,7 @@ __attribute__((deprecated(
/// If `automaticallyLoadReactNativeWindow` is set to `true`, the React Native window will be loaded automatically.
@property (nonatomic, assign) BOOL automaticallyLoadReactNativeWindow;
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter __attribute__((
deprecated("The bridge adapter is deprecated and will be removed when removing the legacy architecture.")));
;
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
- (RCTRootViewFactory *)rootViewFactory;
@@ -6,7 +6,6 @@
*/
#import "RCTAppDelegate.h"
#import <React/RCTBridgeDelegate.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
@@ -40,20 +40,17 @@ std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupDefaultJsExecutor
std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupJsExecutorFactoryForOldArch(
RCTBridge *bridge,
const std::shared_ptr<facebook::react::RuntimeScheduler> &runtimeScheduler)
__attribute__((deprecated(
"RCTAppSetupJsExecutorFactoryForOldArch(RCTBridge *, RuntimeScheduler) is deprecated and will be removed when we remove the legacy architecture.")));
;
const std::shared_ptr<facebook::react::RuntimeScheduler> &runtimeScheduler);
#endif // __cplusplus
RCT_EXTERN_C_BEGIN
void RCTAppSetupPrepareApp(UIApplication *application, BOOL turboModuleEnabled) __attribute__((deprecated(
"RCTAppSetupPrepareApp(UIApplication, BOOL) is deprecated and it's signature will change when we remove the legacy arch")));
UIView *
RCTAppSetupDefaultRootView(RCTBridge *bridge, NSString *moduleName, NSDictionary *initialProperties, BOOL fabricEnabled)
__attribute__((deprecated(
"RCTAppSetupDefaultRootView(RCTBridge *, NSString *, NSDictionary *, BOOL) is deprecated and it's signature will change when we remove the legacy arch")));
void RCTAppSetupPrepareApp(UIApplication *application, BOOL turboModuleEnabled);
UIView *RCTAppSetupDefaultRootView(
RCTBridge *bridge,
NSString *moduleName,
NSDictionary *initialProperties,
BOOL fabricEnabled);
RCT_EXTERN_C_END
@@ -54,7 +54,7 @@ RCTAppSetupDefaultRootView(RCTBridge *bridge, NSString *moduleName, NSDictionary
NSArray<NSString *> *RCTAppSetupUnstableModulesRequiringMainQueueSetup(id<RCTDependencyProvider> dependencyProvider)
{
// For oss, insert core main queue setup modules here
return (dependencyProvider != nullptr) ? dependencyProvider.unstableModulesRequiringMainQueueSetup : @[];
return dependencyProvider ? dependencyProvider.unstableModulesRequiringMainQueueSetup : @[];
}
id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass, id<RCTDependencyProvider> dependencyProvider)
@@ -65,11 +65,11 @@ id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass, id<RCTDe
NSArray<NSString *> *classNames = @[];
if (protocol == @protocol(RCTImageURLLoader)) {
classNames = (dependencyProvider != nullptr) ? dependencyProvider.imageURLLoaderClassNames : @[];
classNames = dependencyProvider ? dependencyProvider.imageURLLoaderClassNames : @[];
} else if (protocol == @protocol(RCTImageDataDecoder)) {
classNames = (dependencyProvider != nullptr) ? dependencyProvider.imageDataDecoderClassNames : @[];
classNames = dependencyProvider ? dependencyProvider.imageDataDecoderClassNames : @[];
} else if (protocol == @protocol(RCTURLRequestHandler)) {
classNames = (dependencyProvider != nullptr) ? dependencyProvider.URLRequestHandlerClassNames : @[];
classNames = dependencyProvider ? dependencyProvider.URLRequestHandlerClassNames : @[];
}
NSMutableArray *modules = [NSMutableArray new];
@@ -10,29 +10,27 @@
NS_ASSUME_NONNULL_BEGIN
__attribute__((deprecated(
"RCTArchConfiguratorProtocol is deprecated and will be removed when we remove the legacy architecture.")));
@protocol RCTArchConfiguratorProtocol
/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off.
///
/// @note: This is required to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the Turbo Native Module are enabled. Otherwise, it returns `false`.
- (BOOL)turboModuleEnabled __attribute__((deprecated("This will be removed in a future version of React Native")));
- (BOOL)turboModuleEnabled __attribute__((deprecated("Use RCTIsNewArchEnabled instead")));
/// This method controls whether the App will use the Fabric renderer of the New Architecture or not.
///
/// @return: `true` if the Fabric Renderer is enabled. Otherwise, it returns `false`.
- (BOOL)fabricEnabled __attribute__((deprecated("This will be removed in a future version of React Native")));
- (BOOL)fabricEnabled __attribute__((deprecated("Use RCTIsNewArchEnabled instead")));
/// This method controls whether React Native's new initialization layer is enabled.
///
/// @return: `true` if the new initialization layer is enabled. Otherwise returns `false`.
- (BOOL)bridgelessEnabled __attribute__((deprecated("This will be removed in a future version of React Native")));
- (BOOL)bridgelessEnabled __attribute__((deprecated("Use RCTIsNewArchEnabled instead")));
/// This method controls whether React Native uses new Architecture.
///
/// @return: `true` if the new architecture is enabled. Otherwise returns `false`.
- (BOOL)newArchEnabled __attribute__((deprecated("This will be removed in a future version of React Native")));
- (BOOL)newArchEnabled __attribute__((deprecated("Use RCTIsNewArchEnabled instead")));
@end
NS_ASSUME_NONNULL_END
@@ -78,7 +78,7 @@
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
{
return (self.dependencyProvider != nullptr) ? self.dependencyProvider.thirdPartyFabricComponents : @{};
return self.dependencyProvider ? self.dependencyProvider.thirdPartyFabricComponents : @{};
}
- (void)hostDidStart:(RCTHost *)host
@@ -87,15 +87,13 @@
- (NSArray<NSString *> *)unstableModulesRequiringMainQueueSetup
{
return (self.dependencyProvider != nullptr)
? RCTAppSetupUnstableModulesRequiringMainQueueSetup(self.dependencyProvider)
: @[];
return self.dependencyProvider ? RCTAppSetupUnstableModulesRequiringMainQueueSetup(self.dependencyProvider) : @[];
}
- (nullable id<RCTModuleProvider>)getModuleProvider:(const char *)name
{
NSString *providerName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
return (self.dependencyProvider != nullptr) ? self.dependencyProvider.moduleProviders[providerName] : nullptr;
return self.dependencyProvider ? self.dependencyProvider.moduleProviders[providerName] : nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
@@ -56,10 +56,7 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable };
*
* @returns: a newly created instance of RCTBridge.
*/
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate
launchOptions:(NSDictionary *)launchOptions
__attribute__((deprecated(
"createBridgeWithDelegate:launchOptions: is deprecated and will be removed when removing the legacy architecture.")));
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions;
/**
* It creates a `UIView` starting from a bridge, a module name and a set of initial properties.
@@ -75,9 +72,7 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable };
*/
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps
__attribute__((deprecated(
"createRootViewWithBridge:moduleName:initProps is deprecated and will be removed when removing the legacy architecture.")));
initProps:(NSDictionary *)initProps;
/// 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
@@ -106,13 +101,10 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable };
initialProperties:(NSDictionary *_Nullable)initialProperties
launchOptions:(NSDictionary *_Nullable)launchOptions;
@property (nonatomic, nullable) RCTBridge *bridge
__attribute__((deprecated("The bridge is deprecated and will be removed when removing the legacy architecture.")));
@property (nonatomic, nullable) RCTBridge *bridge;
@property (nonatomic, strong, nonnull) RCTRootViewFactory *rootViewFactory;
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter __attribute__((
deprecated("The bridgeAdapter is deprecated and will be removed when removing the legacy architecture.")));
;
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
@property (nonatomic, weak) id<RCTReactNativeFactoryDelegate> delegate;
@@ -65,15 +65,14 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc
- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock
newArchEnabled:(BOOL)newArchEnabled
turboModuleEnabled:(BOOL)turboModuleEnabled
bridgelessEnabled:(BOOL)bridgelessEnabled __deprecated;
bridgelessEnabled:(BOOL)bridgelessEnabled NS_DESIGNATED_INITIALIZER __deprecated;
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
newArchEnabled:(BOOL)newArchEnabled
turboModuleEnabled:(BOOL)turboModuleEnabled
bridgelessEnabled:(BOOL)bridgelessEnabled __deprecated;
- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock
newArchEnabled:(BOOL)newArchEnabled NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock newArchEnabled:(BOOL)newArchEnabled;
- (instancetype)initWithBundleURL:(NSURL *)bundleURL newArchEnabled:(BOOL)newArchEnabled;

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