mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
149
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da65b27f92 | ||
|
|
24fee29f7a | ||
|
|
6ae49ee9e3 | ||
|
|
ce9f5345e3 | ||
|
|
5da7089e35 | ||
|
|
762bcc6073 | ||
|
|
7f57018e25 | ||
|
|
b95631ed7f | ||
|
|
b9762d568a | ||
|
|
a36dbae486 | ||
|
|
881d8a720f | ||
|
|
1d909efa23 | ||
|
|
3c09d6bfce | ||
|
|
40c194cf47 | ||
|
|
24f010d5b5 | ||
|
|
df7b6ae092 | ||
|
|
016f44518e | ||
|
|
69356b7f21 | ||
|
|
a6b2355b8d | ||
|
|
8869fa4c2c | ||
|
|
84adf268e1 | ||
|
|
d86412dcc6 | ||
|
|
f70c3cae5b | ||
|
|
96e66e7b48 | ||
|
|
cb7cd89a10 | ||
|
|
f363b43856 | ||
|
|
fe7e97a2fd | ||
|
|
2a2f58ad9d | ||
|
|
b41fb6c88e | ||
|
|
8d83da6460 | ||
|
|
81a94f4f14 | ||
|
|
81cb166d10 | ||
|
|
2781888d22 | ||
|
|
04e44f30f7 | ||
|
|
24b0ded3cf | ||
|
|
30e75a51f3 | ||
|
|
d4d1eb9bb1 | ||
|
|
1fed2cfdef | ||
|
|
5dd712fc08 | ||
|
|
0c47e1351f | ||
|
|
f03e75d9be | ||
|
|
6a472c5cf2 | ||
|
|
a05d573f9e | ||
|
|
e344b4914c | ||
|
|
450d982844 | ||
|
|
162208d264 | ||
|
|
a77d8d9d50 | ||
|
|
0b22b955f1 | ||
|
|
3d1a505da5 | ||
|
|
f1d7016be9 | ||
|
|
74995bc90a | ||
|
|
aec7a66ae8 | ||
|
|
bf5c98cf5e | ||
|
|
89f93dc921 | ||
|
|
e6848ba5ba | ||
|
|
e4fc27ec4d | ||
|
|
bb78142011 | ||
|
|
677d82d818 | ||
|
|
8dc2c90ce5 | ||
|
|
d424bb9d7c | ||
|
|
eb5fbdf134 | ||
|
|
145c72f816 | ||
|
|
3575e21f73 | ||
|
|
e2c621c5fc | ||
|
|
e4d8c96786 | ||
|
|
fd1d6b8c65 | ||
|
|
fb737ca7d3 | ||
|
|
3baf562df0 | ||
|
|
c02e3b1c60 | ||
|
|
451ff70da4 | ||
|
|
12550f05c5 | ||
|
|
68db74205a | ||
|
|
5da7ebf99a | ||
|
|
aae3e03e57 | ||
|
|
6c581c9b9b | ||
|
|
ae09827b15 | ||
|
|
ce838a4bcf | ||
|
|
6460d2b3e7 | ||
|
|
b8419b2eed | ||
|
|
3de9892353 | ||
|
|
113337f269 | ||
|
|
7d63235086 | ||
|
|
9f604dbd43 | ||
|
|
92a476661c | ||
|
|
550b0c0ed1 | ||
|
|
105ad855d9 | ||
|
|
880dea858e | ||
|
|
0eb9472c69 | ||
|
|
ac0dbe1ea2 | ||
|
|
a2523a24c7 | ||
|
|
aa53bde21b | ||
|
|
73a2be1243 | ||
|
|
17502dd743 | ||
|
|
221d1eceda | ||
|
|
6d8fd49832 | ||
|
|
13780126d3 | ||
|
|
aafe696453 | ||
|
|
6e06a810f0 | ||
|
|
48ea6867a9 | ||
|
|
7733c335eb | ||
|
|
de82b15b0d | ||
|
|
102062fbc7 | ||
|
|
105beed569 | ||
|
|
43554b5a0f | ||
|
|
b2f624d054 | ||
|
|
e573929d6c | ||
|
|
01b40789ee | ||
|
|
d2723b8ed7 | ||
|
|
69400be4fc | ||
|
|
d658ff521d | ||
|
|
4dd8d3db57 | ||
|
|
737045217b | ||
|
|
e80bdb88be | ||
|
|
e0a5bbe309 | ||
|
|
09b08aa4e0 | ||
|
|
ba1bc639b8 | ||
|
|
c2aa201b1e | ||
|
|
2944752a5d | ||
|
|
0eff2c5a22 | ||
|
|
e88974f199 | ||
|
|
a865975ce3 | ||
|
|
933356e2a6 | ||
|
|
156454ef39 | ||
|
|
fa65b937f5 | ||
|
|
c0eaa7f1be | ||
|
|
61f01ade42 | ||
|
|
fd2ce35172 | ||
|
|
abeee154d5 | ||
|
|
6f1cf004fa | ||
|
|
8fba7ebb5e | ||
|
|
4c3112c8d8 | ||
|
|
35ab62cca3 | ||
|
|
0b7a0092db | ||
|
|
05874c831b | ||
|
|
47882f0000 | ||
|
|
4cb82501a3 | ||
|
|
0d5c026569 | ||
|
|
a18aa481f0 | ||
|
|
bdf01be654 | ||
|
|
4da6ed5caf | ||
|
|
13bc4a2a43 | ||
|
|
c3091ccfed | ||
|
|
398b3b3be4 | ||
|
|
99767d43b0 | ||
|
|
29a0d7c3b2 | ||
|
|
fbe4c0ed34 | ||
|
|
cc5f17d5a2 | ||
|
|
d1b0e9a30b | ||
|
|
da62721061 |
+2
-1
@@ -44,6 +44,7 @@ packages/react-native/flow/
|
||||
|
||||
[options]
|
||||
enums=true
|
||||
as_const=true
|
||||
casting_syntax=both
|
||||
|
||||
emoji=true
|
||||
@@ -95,4 +96,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.253.0
|
||||
^0.255.0
|
||||
|
||||
@@ -43,7 +43,7 @@ runs:
|
||||
shell: powershell
|
||||
run: |
|
||||
if (-not(Test-Path -Path $Env:HERMES_WS_DIR\win64-bin\hermesc.exe)) {
|
||||
choco install --no-progress cmake --version 3.14.7
|
||||
choco install --no-progress cmake --version 3.14.7 --allow-downgrade
|
||||
if (-not $?) { throw "Failed to install CMake" }
|
||||
|
||||
cd $Env:HERMES_WS_DIR\icu
|
||||
|
||||
@@ -421,7 +421,9 @@ jobs:
|
||||
run-e2e-tests: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
|
||||
test_e2e_android_rntester:
|
||||
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
# Temporarily disable RNTester tests on Android as they are quite flaky and they make CI always red
|
||||
# if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
if: ${{ contains(github.ref, 'stable') || inputs.run-e2e-tests }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_android]
|
||||
strategy:
|
||||
|
||||
+392
@@ -1,5 +1,397 @@
|
||||
# Changelog
|
||||
|
||||
## v0.77.0-rc.0
|
||||
|
||||
### Breaking
|
||||
|
||||
- Bridgeless: Make NativeModules.foo load turbomodules (unset turboModuleProxy in bridgeless). ([cc5f17d5a2](https://github.com/facebook/react-native/commit/cc5f17d5a2b185de1e7dec2a56a97b088e4c7a81) by [@RSNara](https://github.com/RSNara))
|
||||
- dev-middleware: Frameworks should specify `serverBaseUrl` relative to the middleware host. ([acf384a72e](https://github.com/facebook/react-native/commit/acf384a72e599691e4c9d53043f1801da01c58fd) by [@robhogan](https://github.com/robhogan))
|
||||
- `position` of sticky headers on `ScrollView` will now be taken into account ([cbab004eb9](https://github.com/facebook/react-native/commit/cbab004eb94f8312e9b10dae1502d3ca8632a006) by [@joevilches](https://github.com/joevilches))
|
||||
- Remove ReactFabricInternals module ([0c21db360c](https://github.com/facebook/react-native/commit/0c21db360cecc6a3265eea50d628106c58b67af8) by [@huntie](https://github.com/huntie))
|
||||
- Make ReactDebugOverlayTags, DebugOverlayTags, Printer, PrinterHolder, NoopPrinter internal ([623d481991](https://github.com/facebook/react-native/commit/623d4819915e7e6fafb8006c8e3b3b796e394974) by [@cortinico](https://github.com/cortinico))
|
||||
- More spec compliant absolute positioning ([0a2dec175e](https://github.com/facebook/react-native/commit/0a2dec175e92185943907f3e1f6073bae6cd663d) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Native looping animation will not send React state update every time it finishes. ([4b035d820d](https://github.com/facebook/react-native/commit/4b035d820d3f1c3c9a98ce4f55bcc95a7ab064bf) by [@dmytrorykun](https://github.com/dmytrorykun))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Removed ReactViewGroup.getBackgroundColor() ([6a472c5cf2](https://github.com/facebook/react-native/commit/6a472c5cf25d72f72ec7e261282d611a5ab0a662) by Thomas Nardone)
|
||||
- ReadableArray non-primitive getters are now correctly typed as optional ([145c72f816](https://github.com/facebook/react-native/commit/145c72f8163048f0eee30d5ce850911f5dad865c) by [@javache](https://github.com/javache))
|
||||
- Remove jsBundleLoader from DefaultReactHost.getDefaultReactHost() ([fbe4c0ed34](https://github.com/facebook/react-native/commit/fbe4c0ed347156a7ea24da29e09083a499c80cf5) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Reduce visibility of TaskCompletionSource class ([4f55161132](https://github.com/facebook/react-native/commit/4f551611326d63b6fb5cd53e63d536c2c6f04647) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Stable API - Make ImageStoreManager internal ([846c4b1ea2](https://github.com/facebook/react-native/commit/846c4b1ea2d139e3f3cbafa2f8ddbf159e3322e4) by [@arushikesarwani94](https://github.com/arushikesarwani94))
|
||||
- Stable API - Make SwipeRefreshLayoutManager internal ([d02da992a4](https://github.com/facebook/react-native/commit/d02da992a4c0893313b9f059a8be228f43fffa7a) by Thomas Nardone)
|
||||
- Undo breaking change of UiManager.eventDispatcher return type ([7817ab5de2](https://github.com/facebook/react-native/commit/7817ab5de2ce324df734a5dfd705f2454937328f) by [@cortinico](https://github.com/cortinico))
|
||||
- Stable API - Make classes inside `com.facebook.react.views.progressbar` internal ([46526fc2fe](https://github.com/facebook/react-native/commit/46526fc2fe1dd0ce937fad35469b64b9fb05eaa2) by [@cortinico](https://github.com/cortinico))
|
||||
- Undo breaking change of UiManager.eventDispatcher return type ([dbe509a23a](https://github.com/facebook/react-native/commit/dbe509a23a974f16804aefea055a24c06910b259) by [@cortinico](https://github.com/cortinico))
|
||||
- Stable API - Make OkHttpCallUtil internal ([abd118a719](https://github.com/facebook/react-native/commit/abd118a719fe0aeb13f980729ff9ce5fb8358b83) by [@arushikesarwani94](https://github.com/arushikesarwani94))
|
||||
- Stable API - Make SimpleSettableFuture internal ([3dec672398](https://github.com/facebook/react-native/commit/3dec672398729cd668aff92249f1fdde231fb928) by [@arushikesarwani94](https://github.com/arushikesarwani94))
|
||||
- Stable API - Make `ClipboardModule` internal ([10f6d5adb5](https://github.com/facebook/react-native/commit/10f6d5adb538a9b20043312fc09b192759100d63) by [@cortinico](https://github.com/cortinico))
|
||||
- Reduce visibility of ReactVirtualTextShadowNode to internal ([496b0a8729](https://github.com/facebook/react-native/commit/496b0a8729b41266683fdd84de5618b23d06ce3d) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Reduce visibility of ReactVirtualTextViewManager to internal ([4a119c4c3a](https://github.com/facebook/react-native/commit/4a119c4c3ac9bf4b234e2942a27a6efd68a801cc) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Stable API - Make classes in `com.facebook.react.views.safeareaview` internal ([39dfbadd5d](https://github.com/facebook/react-native/commit/39dfbadd5d3468d079bf3b039401237c6e7f363c) by [@cortinico](https://github.com/cortinico))
|
||||
- Stable API - Make ClipboardModule internal ([a72c35f98c](https://github.com/facebook/react-native/commit/a72c35f98ccd1a49d367678b46a29c814ad5de43) by [@cortinico](https://github.com/cortinico))
|
||||
- Undo a breaking change on ReactViewGroup constructor ([bbe714e487](https://github.com/facebook/react-native/commit/bbe714e487fb51c61820fdc5d89c9782da79b657) by [@cortinico](https://github.com/cortinico))
|
||||
- Stable API - Make NativeModulePerfLogger internal ([d7d5de9f96](https://github.com/facebook/react-native/commit/d7d5de9f9630e7dcd0194830d7ccb32f167b619c) by [@cortinico](https://github.com/cortinico))
|
||||
- Make `DevLoadingModule` internal ([8c50bf0beb](https://github.com/facebook/react-native/commit/8c50bf0beb17ced7fdafeae7a734edfc03e6e0b2) by [@cortinico](https://github.com/cortinico))
|
||||
- Stable API - Convert to Kotlin and make internal `NotThreadSafeViewHierarchyUpdateDebugListener` ([287e200332](https://github.com/facebook/react-native/commit/287e20033207df5e59d199a347b7ae2b4cd7a59e) by [@cortinico](https://github.com/cortinico))
|
||||
- Rename DevSupportManagerBase.getCurrentContext() -> getCurrentReactContext() ([0e7ba9094e](https://github.com/facebook/react-native/commit/0e7ba9094ea573d1512b2dc71e46f55b24201b7a) by [@RSNara](https://github.com/RSNara))
|
||||
- Make DevSupportManagerBase.getCurrentReactContext() public ([5a6a42c7d0](https://github.com/facebook/react-native/commit/5a6a42c7d029d44799cb907c0ca3c8aa38fa1770) by [@RSNara](https://github.com/RSNara))
|
||||
- Reduce visibility of ReactUnimplementedViewManager to internal ([fe656be26e](https://github.com/facebook/react-native/commit/fe656be26e0d71bf2505032578dbea54af36b2c5) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Stable API - Make InteropModuleRegistry internal ([cba1d4bae7](https://github.com/facebook/react-native/commit/cba1d4bae7285ba160b05f31ca18cfa0b91e9ef5) by [@cortinico](https://github.com/cortinico))
|
||||
- Stable API - Make ReactDevToolsSettingsManagerModule and ReactDevToolsRuntimeSettingsModule internal ([d7550293a2](https://github.com/facebook/react-native/commit/d7550293a2530f02f75b4249b7e6003edaf28ac9) by [@cortinico](https://github.com/cortinico))
|
||||
- Added JSBundleLoader as parameter of DefaultReactHost ([143b9d172c](https://github.com/facebook/react-native/commit/143b9d172ca755e2457e8dfc6009eeef2475a4a1) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Deleting deprecated CompositeReactPackage ([2cb5198f1b](https://github.com/facebook/react-native/commit/2cb5198f1b589a57a4cfd3cc40a9ed6224fc5d75) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Make ReactHost.createSurface() method non nullable ([70c7616535](https://github.com/facebook/react-native/commit/70c761653564653bdfcb77a8c5ef3608dafcf5d7) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Stable API - Make AppStateModule internal ([dbf09fbe58](https://github.com/facebook/react-native/commit/dbf09fbe5823d77556b451317c14f833735067d9) by [@cortinico](https://github.com/cortinico))
|
||||
- Undo breaking change on UIManager eventDispatcher accessor ([dc1a498d39](https://github.com/facebook/react-native/commit/dc1a498d3974680f2257efd8ff9ec3f989204c68) by [@cortinico](https://github.com/cortinico))
|
||||
- Make `AccessibilityInfoModule` internal ([7168a9d1a2](https://github.com/facebook/react-native/commit/7168a9d1a2ed7459ebb056ebe45d86de791013e3) by [@cortinico](https://github.com/cortinico))
|
||||
- Undo breaking change on ViewManagerDelegate.kt String params ([2d2bb9c588](https://github.com/facebook/react-native/commit/2d2bb9c58824af34392bda196fbf0be17498cc05) by [@cortinico](https://github.com/cortinico))
|
||||
- Add 3 methods to ReactInstanceDevHelper ([c867aba2f3](https://github.com/facebook/react-native/commit/c867aba2f3dd7773be9d2ee0827bcd69481a394e) by [@cortinico](https://github.com/cortinico))
|
||||
- Add test case for set opacity in new arch examples ([848471b69f](https://github.com/facebook/react-native/commit/848471b69f59094780ace25c6963e480d94ec5c3) by [@poonamjain96](https://github.com/poonamjain96))
|
||||
- Add test for Console.log measure in new-arch-examples ([8cf27e8b6f](https://github.com/facebook/react-native/commit/8cf27e8b6f112d646111ddaba0b5036cafac3299) by [@poonamjain96](https://github.com/poonamjain96))
|
||||
- Remove Deprecated DefaultDevSupportManagerFactory.create() ([f25abe51ce](https://github.com/facebook/react-native/commit/f25abe51ce93b97ef1fe97af5fc76f13a5a6ff03) by [@cortinico](https://github.com/cortinico))
|
||||
- Remove BaseViewManagerInterface ([7fb3d830be](https://github.com/facebook/react-native/commit/7fb3d830beae3daf431ac90e9326b744ff8300a1) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Chore: test case added for Image ([d0e5053528](https://github.com/facebook/react-native/commit/d0e50535287088030e60e131691c8d9ed1d57989) by [@sarthak-d11](https://github.com/sarthak-d11))
|
||||
- Remove ReactNativeFlipper object, deprecated in 0.75 ([d1a256f51a](https://github.com/facebook/react-native/commit/d1a256f51a43f465823bdfb8a70fae5f92473d7c) by [@cortinico](https://github.com/cortinico))
|
||||
- Add test for background color and border width in text.yml ([fa62b7e1ed](https://github.com/facebook/react-native/commit/fa62b7e1ed00c69819d85819c6209fb42bedb233) by [@muskan27797](https://github.com/muskan27797))
|
||||
- Added Submit Application test to button.yml ([699f73a6b0](https://github.com/facebook/react-native/commit/699f73a6b0734fe716b01d263e9579bca38c4652) by [@shubhamguptadream11](https://github.com/shubhamguptadream11))
|
||||
- Use BackgroundStyleApplicator when setting background color in BaseViewManager ([309cdea337](https://github.com/facebook/react-native/commit/309cdea337101cfe2212cfb6abebf1e783e43282) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Delete useTurboModules, enableFabricRenderer and enableBridgelessArchitecture fields from ReactFeatureFlags class ([10a33e0479](https://github.com/facebook/react-native/commit/10a33e04793befbeab6ae82d4068b1ebd7fdffd0) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Remove ReactViewBackgroundManager and ReactViewBackgroundDrawable ([51673e41ae](https://github.com/facebook/react-native/commit/51673e41ae12f06b2b378e048094ad39ca30c317) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Use the RCTDependencyProvider in the RCTAppDelegate, breaking the dependency with Codegen ([b91626af26](https://github.com/facebook/react-native/commit/b91626af2626bf611958b1e7d0dd0b7338ccc29e) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Delete experimental API RCTConstants.RCTGetMemoryPressureUnloadLevel ([d79dc48abd](https://github.com/facebook/react-native/commit/d79dc48abd2e65e4d203e488f99eb554c473a1fc) by [@mdvacca](https://github.com/mdvacca))
|
||||
- ([6fc500ee99](https://github.com/facebook/react-native/commit/6fc500ee9942e51519545c1ba1026bacad03dc61) by [@philIip](https://github.com/philIip))
|
||||
- ([538bff710f](https://github.com/facebook/react-native/commit/538bff710f7afb4afa7ce91f20074668ad5c739d) by [@philIip](https://github.com/philIip))
|
||||
- Replace uses of `CGColorRef` with UIColor to avoid manual memory management ([b70709dbc2](https://github.com/facebook/react-native/commit/b70709dbc27c75c69e9fd2b082ffa27e7e8db7fd) by [@Saadnajmi](https://github.com/Saadnajmi))
|
||||
- Cocoapods decide the C++ version for iOS pods ([bd50c4a460](https://github.com/facebook/react-native/commit/bd50c4a460a01dad3cd7cade8191273563675d24) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Delete partialBatchDidFlush ([a777a8937e](https://github.com/facebook/react-native/commit/a777a8937ee165993d7d9b35c7c981fe6698f9dc) by [@philIip](https://github.com/philIip))
|
||||
- We deprecated RCTRuntimeExecutor in 0.76, so let's get rid of the code now. ([cf8d09b279](https://github.com/facebook/react-native/commit/cf8d09b279c4bd667d96f9445c1c0b9acb0a0dfc) by [@philIip](https://github.com/philIip))
|
||||
- Unbreak `RCTHermesInstance` constructor breaking change ([aec6666bfa](https://github.com/facebook/react-native/commit/aec6666bfabf93cde70fa869b3eea68590998dee) by [@tido64](https://github.com/tido64))
|
||||
|
||||
### Added
|
||||
|
||||
- Add FuseboxPerfettoDataSource to emit Fusebox traces using Perfetto ([ef0ea4d834](https://github.com/facebook/react-native/commit/ef0ea4d834ec61c5deede487b46a787a363f6a64) by Benoit Girard)
|
||||
- Add support for `rn_rootThreshold` in Intersection Observer ([a77d8d9d50](https://github.com/facebook/react-native/commit/a77d8d9d50e69a6a4563737c2ce68d26204eda7f) by [@lunaleaps](https://github.com/lunaleaps))
|
||||
- Codegen: Support negative values in enums ([177bf4d043](https://github.com/facebook/react-native/commit/177bf4d043ce862911e477e0f0039214d8541cfd) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Use the TypeScript key in syntax to restrict permissions and types of results ([0244710c4b](https://github.com/facebook/react-native/commit/0244710c4b1e5016069ec9f7b8e7116ff51f59f1) by [@qnnp-me](https://github.com/qnnp-me))
|
||||
- Image `resizeMode` and `objectFit` support for `'none'`. ([d8cfd98070](https://github.com/facebook/react-native/commit/d8cfd98070cbccc5e8a49446d76bdc2cb0c6939f) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
- Add NumberLiteralTypeAnnotation support ([dd472101b7](https://github.com/facebook/react-native/commit/dd472101b76a964b7a64f0bb84d967f702500cad) by [@elicwhite](https://github.com/elicwhite))
|
||||
- Eliminate usage of more than 1-arg `React.AbstractComponent` in React codebase ([6205aad81e](https://github.com/facebook/react-native/commit/6205aad81ef8154a106fa253a1f4b00aee568650) by [@SamChou19815](https://github.com/SamChou19815))
|
||||
- Added support for `display: contents` ([e7a3f479fe](https://github.com/facebook/react-native/commit/e7a3f479fe37a5d503770bafc03c80b1c2dcb8f7) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
- Added the missing `announceForAccessibilityWithOptions` function mock in the `packages/react-native/jest/setup.js` file. ([4ae999b90b](https://github.com/facebook/react-native/commit/4ae999b90b2a67233890d30a8bff0dbdd58364f2) by [@ChrisMattew](https://github.com/ChrisMattew))
|
||||
- Add support for reload-to-profile in Fusebox (Part 2 of 2: JS) ([4df224ca6d](https://github.com/facebook/react-native/commit/4df224ca6df9dc593ab7090520578f27c520f361) by [@EdmondChuiHW](https://github.com/EdmondChuiHW))
|
||||
- Add JS layer for new text content type cellular EID and cellular IMEI ([118c1f7035](https://github.com/facebook/react-native/commit/118c1f7035cfcadb567b1ccd40d85c751dd65162) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Add support for reload-to-profile in Fusebox. (Part 1 of 2: native) ([91a40a28de](https://github.com/facebook/react-native/commit/91a40a28de3f74a389c6e6ad05fb917d58fc072d) by [@EdmondChuiHW](https://github.com/EdmondChuiHW))
|
||||
- Add cli --help details to combine-js-toschema-cli.js ([e4814b0d6d](https://github.com/facebook/react-native/commit/e4814b0d6d981cf96eef2803e8f20548263ea0e4) by [@blakef](https://github.com/blakef))
|
||||
- Codegen for Native Modules now supports string literals ([d2f3f06826](https://github.com/facebook/react-native/commit/d2f3f06826e2287ad5f5dc4e27201fc5dc9fcd5c) by [@elicwhite](https://github.com/elicwhite))
|
||||
- Codegen now supports Union Types in NativeModules ([3af126b562](https://github.com/facebook/react-native/commit/3af126b562cf7e0828906a4fe0ac79a886b557e6) by [@elicwhite](https://github.com/elicwhite))
|
||||
- Updated FlatList setNativeProps type ([b0ac99b477](https://github.com/facebook/react-native/commit/b0ac99b47781e3b68bee2ff737b109f58eeeba78) by [@JDMathew](https://github.com/JDMathew))
|
||||
- Added Jest mocks for `NativeAnimatedModule` ([fd355308fa](https://github.com/facebook/react-native/commit/fd355308faa2da30d4e0d68ae9b4d136d2a5f1c3) by [@yungsters](https://github.com/yungsters))
|
||||
- Added `HostInstance` type to represent the instance of a `HostComponent<T>`. ([e24f9917c2](https://github.com/facebook/react-native/commit/e24f9917c25ed8c32c6e01d50a5fa49fe66a6ee1) by [@yungsters](https://github.com/yungsters))
|
||||
- Export `DevMenu` from `react-native` ([e12c0d9551](https://github.com/facebook/react-native/commit/e12c0d95516924b5e6ca4f0d5ebcadb42bb19f30) by [@frankcalise](https://github.com/frankcalise))
|
||||
- Removed `experimental` prefix and fully released `mixBlendMode` prop ([d2c48f3b1a](https://github.com/facebook/react-native/commit/d2c48f3b1a2a15e9832ea3240c19aea5186ceb24) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- Add Playground to RNTester for testing out features & submitting reproducers. ([256adcab7b](https://github.com/facebook/react-native/commit/256adcab7b02e8a80227d44b76d1a74d8c4973de) by [@shwanton](https://github.com/shwanton))
|
||||
- Add color formatting to codegen output ([1747f57c67](https://github.com/facebook/react-native/commit/1747f57c6708ffa59ea511f9fbbacf37364fde72) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Added overlayColor prop to modal component for customisable background overlay ([4e1d7015c1](https://github.com/facebook/react-native/commit/4e1d7015c1d4e2703133b1347ed97a0cc93b1318) by [@shubhamguptadream11](https://github.com/shubhamguptadream11))
|
||||
- Created an experimental optimization to improve memoization within `Animated` to improve product performance ([dc84d00db6](https://github.com/facebook/react-native/commit/dc84d00db64b78234e91c2b73f4d4fce8782cd02) by [@yungsters](https://github.com/yungsters))
|
||||
- Expose missing text content type to JS ([d3d48cb357](https://github.com/facebook/react-native/commit/d3d48cb357e55b1f2ae0042a5960451fbe5ff5c7) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Created an experimental (unstable) method for allowlisting props when using `Animated` ([f3f652daab](https://github.com/facebook/react-native/commit/f3f652daabc4a1d374190165ac38715ad31a5214) by [@yungsters](https://github.com/yungsters))
|
||||
- Upgrade Flow to 0.245.2 ([1f65bf9545](https://github.com/facebook/react-native/commit/1f65bf9545cd84bbf1be7a7a91cb0b4f8b4b47ba) by [@SamChou19815](https://github.com/SamChou19815))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Image `force-cache` caching control option ([a0be88fd72](https://github.com/facebook/react-native/commit/a0be88fd727898d4626ca51876d0bfb4e50dcb77) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
- Add overlapping radii resolution logic preventing incorrect rendering ([451ff70da4](https://github.com/facebook/react-native/commit/451ff70da422beefccfff5b87024145003bb49ff) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- Marked ReactPackage#getModule as stable. ([8fba7ebb5e](https://github.com/facebook/react-native/commit/8fba7ebb5e874d001dc5c16eb4229054a2ef4812) by [@javache](https://github.com/javache))
|
||||
- ReactDelegate and ReactActivityDelegate#getCurrentReactContext can be used to access the current context ([fe8cc62824](https://github.com/facebook/react-native/commit/fe8cc62824372229d90445b2769fed9c08604a7e) by [@javache](https://github.com/javache))
|
||||
- Add exceptionHandler as a parameter of DefaultReactHost.getDefaultReactHost() method ([7a5a10c95c](https://github.com/facebook/react-native/commit/7a5a10c95ce6af1964ce5bd273c0c6513fb78a1e) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Logic to check for grayscale mode on android ([e70202e606](https://github.com/facebook/react-native/commit/e70202e606be3101da33d28f5403443c3e84749e) by [@oddlyspaced](https://github.com/oddlyspaced))
|
||||
- TextTransform ktx ([7794d7af43](https://github.com/facebook/react-native/commit/7794d7af43b90b581574361af102f67ad7961a5e) by Thomas Nardone)
|
||||
- Add navigationBarTranslucent prop to Modal component ([7a6c7a462a](https://github.com/facebook/react-native/commit/7a6c7a462a7898e13327438a6603d83a39147abb) by [@zoontek](https://github.com/zoontek))
|
||||
- Trigger Java GC on app reload ([de3c1ee097](https://github.com/facebook/react-native/commit/de3c1ee097f7041d6f9754a55a45cea80fd25813) by [@mrousavy](https://github.com/mrousavy))
|
||||
- Add cmake arguments to support 16KB page size for native libraries ([65cdd5b82c](https://github.com/facebook/react-native/commit/65cdd5b82ce7652630b1920fa3a48c8f256c7983) by [@alanleedev](https://github.com/alanleedev))
|
||||
- Enabling basic `Image` cache control for Android ([e5dd7d68bf](https://github.com/facebook/react-native/commit/e5dd7d68bf264669fc5c4ce5e69b24249d28558b) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
- Add OnLayoutChange API for scroll views ([d825a4d712](https://github.com/facebook/react-native/commit/d825a4d712a1ba53c0f4209c9da5d51578209a6d) by Thomas Nardone)
|
||||
- React Activity exposes ReactHost ([d78cb78b15](https://github.com/facebook/react-native/commit/d78cb78b15a520f3bebcdf00b82b26bf120f4180) by [@shwanton](https://github.com/shwanton))
|
||||
- Added PixelUtil extensions for Int and Long ([9406a09f87](https://github.com/facebook/react-native/commit/9406a09f871d3efa66e3d10237a6d1fd08ddee0b) by Thomas Nardone)
|
||||
- Add boxShadow support to BaseViewManager ([b69a92e2c9](https://github.com/facebook/react-native/commit/b69a92e2c95990fcfa95a6ab7b7260ac492f942c) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Test: pressable test cases ([3102a58df3](https://github.com/facebook/react-native/commit/3102a58df38d96f3dacef0530e4dbb399037fcd2) by [@deepanshushuklad11](https://github.com/deepanshushuklad11))
|
||||
- Added showcase for Android `resizeMethod` options when applied to a large image ([768a1d8664](https://github.com/facebook/react-native/commit/768a1d86649e391964d404a15733c227c57c0d38) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Adds a new `resizeMethod`, `none`, which disables downsampling for an image ([6202319ed5](https://github.com/facebook/react-native/commit/6202319ed544e4d23f1f327ff5334c480af3819d) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Add Modal Testing to modal.yml ([1a2532b1fc](https://github.com/facebook/react-native/commit/1a2532b1fc32ec989e05576121b5e669697d29af) by [@ahmedbilal008](https://github.com/ahmedbilal008))
|
||||
- Add a new Fresco decoder for XML resource types ([118b7c18a6](https://github.com/facebook/react-native/commit/118b7c18a6f827dabb00522bbd6a2416e1bf99da) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Added `isHighTextContrastEnabled()` to `AccessibilityInfo` to read `ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED` setting value ([d4ea147b41](https://github.com/facebook/react-native/commit/d4ea147b41e4d22253f80be8b74731dcf0439302) by Ariel Lin)
|
||||
- Outline properties `outline-width`, `outline-color`, `outline-style` & `outline-offset` ([17faac4170](https://github.com/facebook/react-native/commit/17faac417035f2cc5f12ec60a46270991be0989a) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- Flag in ReactFragment to allow unmounting a surface without destroying ReactHost. ([bb3ec4d6c3](https://github.com/facebook/react-native/commit/bb3ec4d6c342d3afcb109e8c65f99e7693c962b5) by [@rozele](https://github.com/rozele))
|
||||
- ReactDelegate `unloadApp` methods for unmounting surfaces without destroying ReactHost ([38593c440e](https://github.com/facebook/react-native/commit/38593c440e949a9b52df0a6821b80ac47b19a693) by [@rozele](https://github.com/rozele))
|
||||
- New feature flag for line height calculation ([f338e7c0a1](https://github.com/facebook/react-native/commit/f338e7c0a1072974b5ac76567c8d1d06f5b7c1e2) by [@mellyeliu](https://github.com/mellyeliu))
|
||||
- Adds support for importing XML assets as images ([2e80f5acf1](https://github.com/facebook/react-native/commit/2e80f5acf11cd9726921a00a58700cab9e0bb0a7) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Image `only-if-cached` cache control option ([dc9db01665](https://github.com/facebook/react-native/commit/dc9db01665308ac931967326abfc86deb9ae7e2a) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
- TextInput `inputAccessoryViewButtonLabel` prop ([32931466ed](https://github.com/facebook/react-native/commit/32931466ed7e3d8d9eeeb65f12ce146e123870ba) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
- Fix: Correct Layout Behavior for Combined align-content and align-items ([73a2be1243](https://github.com/facebook/react-native/commit/73a2be12437c93bbd138e0d63cd45d834529a3c4) by [@phuccvx12](https://github.com/phuccvx12))
|
||||
- Extract RCTAppDependencyProvider in the ReactAppDependencyProvider pod ([102062fbc7](https://github.com/facebook/react-native/commit/102062fbc7d6a5c81192562c3ccb87bc4222a0cf) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Pass the `RCTAppDependencyProvider` to the `RCTAppDelegate` ([95fc906930](https://github.com/facebook/react-native/commit/95fc906930ece9b30482eb1f0924d7e8614af7c6) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Introduce the RCTAppDependencyProvider to minimize the changes required y the users ([41c2502b36](https://github.com/facebook/react-native/commit/41c2502b3650e238b0a5d86b5044abcb538b76e3) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Add RCTDependencyProvider protocol ([f2b3716426](https://github.com/facebook/react-native/commit/f2b371642684682916522dc82adee93464ba31fe) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Added `disabledButtonTintColor` prop to ActionSheetIOS ([089c87e22e](https://github.com/facebook/react-native/commit/089c87e22e9f431d0e7f35292dce2e36fa86d09a) by [@henninghall](https://github.com/henninghall))
|
||||
- Scroll the cursor into view when text input is focused ([e021e50d53](https://github.com/facebook/react-native/commit/e021e50d537884a55a9f5bea931adb19e9069dd6) by [@dominictb](https://github.com/dominictb))
|
||||
- Introduce RCTArchConfiguratorProtocol ([ec0dbb729d](https://github.com/facebook/react-native/commit/ec0dbb729d3774184dee83efbff5f9beebce72ce) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Introduce RCTUIConfiguratorProtocol ([8850736188](https://github.com/facebook/react-native/commit/8850736188321f13c7bdeadedeed15c759ad72c1) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- TvOS support for Hermes artifacts ([f673759c83](https://github.com/facebook/react-native/commit/f673759c83fed130964ea7dfe677c04608b8e64d) by [@douglowder](https://github.com/douglowder))
|
||||
- Add `CallInvoker` to `BindingsInstaller` ([87bae7f734](https://github.com/facebook/react-native/commit/87bae7f7349976cfc269b25584330c4d3d897d79) by [@mrousavy](https://github.com/mrousavy))
|
||||
- Added `isDarkerSystemColorsEnabled()` to `AccessibilityInfo` to read "Increase Contrast" setting value ([af3bee6511](https://github.com/facebook/react-native/commit/af3bee6511fe72fda7415bf974f937012b3eefec) by Ariel Lin)
|
||||
- Expose RCT_NEW_ARCH_ENABLED to Swift ([d24507611d](https://github.com/facebook/react-native/commit/d24507611de7fefc71286fd54b2c909d98a4bf27) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Add `systemCyan` and `systemMint` colors on iOS ([4caf548a9f](https://github.com/facebook/react-native/commit/4caf548a9f2e485aef9cbaeeb0a666cf9b9d3e59) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Outline properties `outline-width`, `outline-color`, `outline-style` & `outline-offset` ([1288e38423](https://github.com/facebook/react-native/commit/1288e38423f93ed57737dd9b40ad55696494d6f4) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- Declare supportedInterfaceOrientations only on iOS ([40c5e6b64a](https://github.com/facebook/react-native/commit/40c5e6b64a1d82b5b481717b9086340eca2aaef9) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- New text content type cellular EID and cellular IMEI ([14e0d0dffb](https://github.com/facebook/react-native/commit/14e0d0dffb9ee8b1d41d60ccbcfd63d53bfe4d48) by [@pasc0al](https://github.com/pasc0al))
|
||||
- User-configurable BUNDLE_NAME when building bundles ([f8287e25e1](https://github.com/facebook/react-native/commit/f8287e25e11d56beb66ace414ca5f8a6a32405a9) by [@fivecar](https://github.com/fivecar))
|
||||
- Line break mode for TextInput components. **This includes JS APIs for the new mode.** ([ce2d34f194](https://github.com/facebook/react-native/commit/ce2d34f19451c91a90f0bd820064208912c86092) by [@shubhamguptadream11](https://github.com/shubhamguptadream11))
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed a typo in the README file. ([30e75a51f3](https://github.com/facebook/react-native/commit/30e75a51f3af7fbad83e4b002526b69bba021753) by [@Aryan2727-debug](https://github.com/Aryan2727-debug))
|
||||
- Fix unnececary rerenders of VirtualizedListCells with strictMode={true} ([aafe696453](https://github.com/facebook/react-native/commit/aafe696453186d0e87ae96d0bca4c6650234d222) by [@Tom910](https://github.com/Tom910))
|
||||
- The `AnimatedNode` graph will not occur during the insertion effect phase, which means animations can now be reliably started during layout effects. ([316170ce8d](https://github.com/facebook/react-native/commit/316170ce8d0aac1df3261c792b9f768665d134c5) by [@yungsters](https://github.com/yungsters))
|
||||
- AttributedString `appendFragment` and `prependFragment` take an rval instead of a const ref; append/prependAttributedString have been removed ([2c31fe99e1](https://github.com/facebook/react-native/commit/2c31fe99e1cc5bfdb393d4f5c70231a042ea67ef) by [@javache](https://github.com/javache))
|
||||
- Improved AttributedText generation for raw text nodes. ([2f7957f2fd](https://github.com/facebook/react-native/commit/2f7957f2fd424fdaa980d99a4ff05eb3237d662e) by [@javache](https://github.com/javache))
|
||||
- Callbacks passed to `animation.start(<callback>)` will be scheduled for execution in a microtask. Previously, there were certain scenarios in which the callback could be synchronously executed by `start`. ([090501d26d](https://github.com/facebook/react-native/commit/090501d26d83be5c5d2df885c60d05ec133d7a54) by [@yungsters](https://github.com/yungsters))
|
||||
- Optimized the performance of updating `Animated` components. ([f0ffcd4f5d](https://github.com/facebook/react-native/commit/f0ffcd4f5dfeab794fbfa3257bb9a8fd793ff2bc) by [@yungsters](https://github.com/yungsters))
|
||||
- Bump serve-static to 1.16.2 to fix CVE-2024-43800 ([50e38cc9f1](https://github.com/facebook/react-native/commit/50e38cc9f1e6713228a91ad50f426c4f65e65e1a) by [@cortinico](https://github.com/cortinico))
|
||||
- Bump Folly to 2024.10.14.00 ([37375d8aba](https://github.com/facebook/react-native/commit/37375d8aba0869531567426466478b0de9a1aea3) by [@alanleedev](https://github.com/alanleedev))
|
||||
- `useMergeRefs` and components using it (e.g. `Pressable`) now support ref cleanup functions. ([01e210fd28](https://github.com/facebook/react-native/commit/01e210fd28bc961e8c1b5fa454b3c947adda296c) by [@yungsters](https://github.com/yungsters))
|
||||
- : Update Metro to 0.81.0 ([0902b0af75](https://github.com/facebook/react-native/commit/0902b0af75ba30ec9d4abeda71769d0685637afa) by [@robhogan](https://github.com/robhogan))
|
||||
- Animations started with incompatible `useNativeDriver` and `AnimatedValue` configurations will now synchronously fail. Previously, spring and timing animations with non-zero delays would throw the error asynchronously. ([fd8cf19625](https://github.com/facebook/react-native/commit/fd8cf1962585aa49e8921475d89c1a5c2423c51c) by [@yungsters](https://github.com/yungsters))
|
||||
- The `Animation` superclass no longer exposes `__onEnd` as a property. Subclasses must instead invoke `super.start(…)` in their `start()` implementation. ([b3fe06b268](https://github.com/facebook/react-native/commit/b3fe06b2685e526fe2bc420a902a3ccd4e1381df) by [@yungsters](https://github.com/yungsters))
|
||||
- Simplified Flow types to use `HostInstance` (which changing nominal types). ([177697f539](https://github.com/facebook/react-native/commit/177697f539ec68a47cbb8f57260cebe701589ef1) by [@yungsters](https://github.com/yungsters))
|
||||
- Upgrade React DevTools to 6.0.0. ([ed4f6d6891](https://github.com/facebook/react-native/commit/ed4f6d68910677ab050e3fef5aaa87e42d582fe0) by [@hoxyq](https://github.com/hoxyq))
|
||||
- Fix: use public instance in Fiber renderer and expose it from getInspectorDataForViewAtPoint (#31068) ([633ad4933e](https://github.com/facebook/react-native/commit/633ad4933e9514d4168d6dcdb7e56c9a1859482a) by [@hoxyq](https://github.com/hoxyq))
|
||||
- Get react-native package name from package.json in codegen script ([65575e8399](https://github.com/facebook/react-native/commit/65575e8399a08841a0ccbc5b28cafeee6d41714c) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Bring back shouldSkipStateUpdatesForLoopingAnimations feature flag ([6e0e712c2a](https://github.com/facebook/react-native/commit/6e0e712c2a680cf4b4de1ee5fb25cc5282b242c5) by [@dmytrorykun](https://github.com/dmytrorykun))
|
||||
- AnimatedNode (and its subclasses) once again implement `toJSON()`. ([7bd4a54968](https://github.com/facebook/react-native/commit/7bd4a5496815943b031b68ca46792560d8d798d8) by [@yungsters](https://github.com/yungsters))
|
||||
- Do not print Bridgeless Mode is enabled on console anymore ([f3a969f38d](https://github.com/facebook/react-native/commit/f3a969f38d7a2c74bf63ee05c4c819c0873059a9) by [@cortinico](https://github.com/cortinico))
|
||||
- Bump main to 0.77-main ([120558c0cd](https://github.com/facebook/react-native/commit/120558c0cd6ae50156d2f7b958d3a29fd7ae4623) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Improved the performance of unmounting (and updating, when an enclosing Activity becomes hidden) Animated components ([46abda55b9](https://github.com/facebook/react-native/commit/46abda55b9749dc171e0fec551ba83c027818eb7) by Royi Hagigi)
|
||||
- AnimatedNode (and its subclasses) no longer implement `toJSON()`. ([fe6228512e](https://github.com/facebook/react-native/commit/fe6228512e076820c9c11869cfe24b19d1abfb58) by [@yungsters](https://github.com/yungsters))
|
||||
- Animated now resolves `style` to the original prop value if it contains no `AnimatedNode` instances. Previously, it would resolve to a flattened style object. ([ca234ba10e](https://github.com/facebook/react-native/commit/ca234ba10e8d06630da7ea00aac515b222137645) by [@yungsters](https://github.com/yungsters))
|
||||
- Bump Metro dependencies to 0.81.0-alpha.0 ([40f98b5eee](https://github.com/facebook/react-native/commit/40f98b5eeef18bb9c20a9a2762d0b1d648c4ae91) by [@robhogan](https://github.com/robhogan))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Consolidated ReactViewGroup add/remove overrides ([0b22b955f1](https://github.com/facebook/react-native/commit/0b22b955f1494f0e7441a01dd19b3fdf5e20c3dc) by Thomas Nardone)
|
||||
- Migrated systeminfo module code from Java to Kotlin ([8dc2c90ce5](https://github.com/facebook/react-native/commit/8dc2c90ce5f4b30f4729560aed2412e6d29f39fa) by [@oddlyspaced](https://github.com/oddlyspaced))
|
||||
- Updating targetSdk to 35 (apps can still choose their own targetSdk regardless of RN version) ([48ea6867a9](https://github.com/facebook/react-native/commit/48ea6867a96bd16dc7aed9af5a8e9ce12a487c22) by [@alanleedev](https://github.com/alanleedev))
|
||||
- Invocations to JS will now invoke their callbacks immediately if the instance is ready. Surface starts will not wait for the main thread to become available to dispatch the work in JS. ([9fa4845136](https://github.com/facebook/react-native/commit/9fa4845136969ec95ce5615b7ea78feaf0f7f109) by [@javache](https://github.com/javache))
|
||||
- TurboModules marked as requiring eager init will now be constructed on the mqt_native thread to increase concurrency in React Native init. ([663b5f9d19](https://github.com/facebook/react-native/commit/663b5f9d19a905520bd0aef62cda890f76ef9b6e) by [@javache](https://github.com/javache))
|
||||
- Reduce visibility of Continuation to internal, although this interface wasn't being exposed in any public API ([7b21b9e69c](https://github.com/facebook/react-native/commit/7b21b9e69c6176c7f66af0e479623ed1c2be0d62) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Update documentation for ReactHost.destroy() APIs ([443bc32dc4](https://github.com/facebook/react-native/commit/443bc32dc49a86534fce1d6ec0e31c97b8f33053) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Deprecate CatalystInstance in old architecture ([3e27ef1f6e](https://github.com/facebook/react-native/commit/3e27ef1f6e024bba8725a3bd64e2648ffd6af496) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Deprecate BridgelessCatalystInstance class ([72bd840dd3](https://github.com/facebook/react-native/commit/72bd840dd3dc7b6e3e88a74ac9ddb000a0cb3a60) by [@mdvacca](https://github.com/mdvacca))
|
||||
- AGP to 8.7.2 ([e1a1cead43](https://github.com/facebook/react-native/commit/e1a1cead434a6856b2b018274876b9ba8eab706a) by [@cortinico](https://github.com/cortinico))
|
||||
- Migrate ReactFeatureFlags to Kotlin ([4076dbfc86](https://github.com/facebook/react-native/commit/4076dbfc8651fcd193b886bf63b23728fad466d7) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Android NDK to 27.1 ([ba061a5d18](https://github.com/facebook/react-native/commit/ba061a5d18176fd455c3aa6350127506dac05211) by [@alanleedev](https://github.com/alanleedev))
|
||||
- Migrate MainReactPackage to Kotlin (and make it final) ([7bbac8ee27](https://github.com/facebook/react-native/commit/7bbac8ee27daf092b580b57c2c7ee46d2723cd09) by [@cortinico](https://github.com/cortinico))
|
||||
- Bump fbjni to 0.7.0 ([1c002c7b4e](https://github.com/facebook/react-native/commit/1c002c7b4ea4f22729920e54a9032402247ea350) by [@alanleedev](https://github.com/alanleedev))
|
||||
- Fresco to 3.4.0 ([091025e18b](https://github.com/facebook/react-native/commit/091025e18b1dc5212031e3ed06bfb6c450e788e2) by [@cortinico](https://github.com/cortinico))
|
||||
- Bump Kotlin 1.9.x to 2.0.x ([972c2c864c](https://github.com/facebook/react-native/commit/972c2c864c0b563163a36080a13908d1c0a3fb87) by [@cortinico](https://github.com/cortinico))
|
||||
- Fix: change opacity logic from random to decrement by -0.20 ([af4907025e](https://github.com/facebook/react-native/commit/af4907025ef358c312d469118a52699dcf14507d) by [@sarthak-d11](https://github.com/sarthak-d11))
|
||||
- Bump Android Gradle Plugin (AGP) to 8.7.0 ([cbc0978bb6](https://github.com/facebook/react-native/commit/cbc0978bb65dcfd2a18a54201c845785a626b1b3) by [@cortinico](https://github.com/cortinico))
|
||||
- Improve FpsDebugFrameCallback.getTotalTimeMS() accuracy ([d54c25fdae](https://github.com/facebook/react-native/commit/d54c25fdaee4d2642cdbca812b61c6973d3f203d) by [@aamagda](https://github.com/aamagda))
|
||||
- Update Fresco from 3.2.0 to 3.3.0 ([86d92e41d2](https://github.com/facebook/react-native/commit/86d92e41d2e129d12589848f75dfe2b2db09ed1a) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Disabling `outline` props on Android to stay consistent with iOS ([7ab0002799](https://github.com/facebook/react-native/commit/7ab0002799d52a876a8cdd32f25ebef6d431650e) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- Gradle to 8.10.1 ([90f89a830a](https://github.com/facebook/react-native/commit/90f89a830acced9e6b8e80ef58aefd7e2c9666a8) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Make `prefetchImageWithMetadata`'s `queryRootName` nullable in the spec ([4dd60acb7d](https://github.com/facebook/react-native/commit/4dd60acb7ddc2811453e84e3567c1a114fa5e6f9) by [@TheRogue76](https://github.com/TheRogue76))
|
||||
- Change how components automatically register ([8becc2514d](https://github.com/facebook/react-native/commit/8becc2514d484c99b51ea76f479f06a8fcdd8265) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Stop generating the RCTThirdPartyLibraryComponentProvider ([60b9d3d89e](https://github.com/facebook/react-native/commit/60b9d3d89eba90a945070646c6220b3c03b3aeba) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- `RCTSurfaceHostingProxyRootView` no longer has different behavior (whether it calls `start` on the provided *surface*) depending on which initializer is used. Call `start` yourself on the *surface* instead. ([13b93cfdda](https://github.com/facebook/react-native/commit/13b93cfddaa559697968ac1c19e55f7aaa053070) by Nolan O'Brien)
|
||||
- Use `newArchEnabled` flag in RCTAppDelegate and RCTRootViewFactory ([7e1674fc59](https://github.com/facebook/react-native/commit/7e1674fc59abeeb70d946ee082a829fae4c671b7) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Do not move to the main queue synchronously when starting a new surface ([ab2c47be28](https://github.com/facebook/react-native/commit/ab2c47be285fde99a77d189fd04220ef8ad0933a) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Rename `RCTUIGraphicsImageRenderer` to `RCTMakeUIGraphicsImageRenderer` ([6a09fc09af](https://github.com/facebook/react-native/commit/6a09fc09af8eedbf409ab870d5714e44892b2a16) by [@Saadnajmi](https://github.com/Saadnajmi))
|
||||
- Refactor RCTDevLoadingView to use constraints ([fbda60e657](https://github.com/facebook/react-native/commit/fbda60e65783d9a3f257cd427cbffc1c9b2cc01b) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Deprecated shadows for ReadableNative[Map|Array].[Readable|Writable] ([d424bb9d7c](https://github.com/facebook/react-native/commit/d424bb9d7cf101dc5609e8f787ba8af2a33d0262) by [@javache](https://github.com/javache))
|
||||
- ReactContext.getFabricUIManager() method ([fb737ca7d3](https://github.com/facebook/react-native/commit/fb737ca7d34b636a3aa337a0935bf9a5a10c641d) by [@mdvacca](https://github.com/mdvacca))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Deprecating RCTBridgeModule batchDidComplete and adding configuration to disable it ([731bd95c43](https://github.com/facebook/react-native/commit/731bd95c430c752126aaaa34e4911ba2b87b382f) by [@philIip](https://github.com/philIip))
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `BackHandler.removeEventListener` ([44d619414c](https://github.com/facebook/react-native/commit/44d619414c1de3dbf17a421afa8dbcec7cdab025) by [@retyui](https://github.com/retyui))
|
||||
- Removed type for useConcurrentRoot from AppRegistry, as it was already ignored ([2ec547ad28](https://github.com/facebook/react-native/commit/2ec547ad28ca914c17e276abb91174dfa0dc87b2) by [@javache](https://github.com/javache))
|
||||
- Remove "run on iOS" and "run on Android" from the dev server key commands ([19b971ff94](https://github.com/facebook/react-native/commit/19b971ff94a0fe353f82363e788c13f86815663b) by [@huntie](https://github.com/huntie))
|
||||
- Removed `refs` property from `NativeMethods` TypeScript definition. ([223e98cc4b](https://github.com/facebook/react-native/commit/223e98cc4b656b94b48c88940114bfdc025f8ddf) by [@yungsters](https://github.com/yungsters))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Removed hasConstants constructor from ReactModuleInfo ([aec7a66ae8](https://github.com/facebook/react-native/commit/aec7a66ae84bd7689d902686711db8eabc21c1f1) by [@javache](https://github.com/javache))
|
||||
- Removed TurboReactPackage, which was replaced by BaseReactPackage ([bf5c98cf5e](https://github.com/facebook/react-native/commit/bf5c98cf5e1ce5c041d3d9321a0ba2fd3c755b46) by [@javache](https://github.com/javache))
|
||||
- DevToolsReactPerfLogger stats gathering now uses an internal API ([f503fe3f10](https://github.com/facebook/react-native/commit/f503fe3f100d96a31fc56451d7846d80ce5c342f) by [@javache](https://github.com/javache))
|
||||
- BindingImpl is no longer part of the public interface ([18faf68b48](https://github.com/facebook/react-native/commit/18faf68b4825118253cf247572084c4da8f6366e) by [@javache](https://github.com/javache))
|
||||
- FabricComponents is removed from public API ([300db67b27](https://github.com/facebook/react-native/commit/300db67b270a5d6fc85d27188d0d1089675f89c0) by [@javache](https://github.com/javache))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Remove available for RCTKeyWindow check ([e98c221e5e](https://github.com/facebook/react-native/commit/e98c221e5e54e79c25acd06f14e7d01221823305) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Hermes: revert Intl removal ([4cffff35e0](https://github.com/facebook/react-native/commit/4cffff35e030f256c32bf69c5971dfee4e60723f) by [@robhogan](https://github.com/robhogan))
|
||||
- Fix the nodes' owners not being updated when `display: contents` is used ([aa53bde21b](https://github.com/facebook/react-native/commit/aa53bde21b8952533cf17ffee234b273766f2133) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
- dev-middleware: Rewrite URLs in the inspector proxy to cover all configurations, not just Android emulators. ([74995bc90a](https://github.com/facebook/react-native/commit/74995bc90aa039b880e4875ad356d3bce324d902) by [@robhogan](https://github.com/robhogan))
|
||||
- Fix C++ bridging template compatibility with MSVC ([e6848ba5ba](https://github.com/facebook/react-native/commit/e6848ba5ba997d102cbaf6181c7c8c73e25a0827) by [@acoates-ms](https://github.com/acoates-ms))
|
||||
- Accessing KeyboardAvoidingEvent event in onLayout handler ([68db74205a](https://github.com/facebook/react-native/commit/68db74205afdd190304eb73ef71710781fa580b9) by [@mhoran](https://github.com/mhoran))
|
||||
- dev-middleware: Fix URL rewriting where device and debugger reach the server on different ports/protocols. ([5da7ebf99a](https://github.com/facebook/react-native/commit/5da7ebf99ae317c104d8e4bfbf36d3d89f66b5c9) by [@robhogan](https://github.com/robhogan))
|
||||
- dev-middleware: Regex-escape IP addresses in urlRegex replacements ([aae3e03e57](https://github.com/facebook/react-native/commit/aae3e03e57096c3dc51589a3de240d49a8b9fadf) by [@robhogan](https://github.com/robhogan))
|
||||
- Fixed jest error from Appearance.js ([ce838a4bcf](https://github.com/facebook/react-native/commit/ce838a4bcfb1c08728b637a9addd24cc6e3477e0) by [@Kudo](https://github.com/Kudo))
|
||||
- When using Babel with plain JavaScript files, support for additional user syntax plugins should be fixed (now uses Babel's parser instead of hermes-parser). There is no change for JS files annotated with `flow`, where extended JS syntax remains unsupported. ([3de9892353](https://github.com/facebook/react-native/commit/3de989235365504468d2b6c0bb194e944bf1ce8e) by [@huntie](https://github.com/huntie))
|
||||
- Fix `Appearance.setColorScheme(null)` not resetting color scheme value ([7d63235086](https://github.com/facebook/react-native/commit/7d63235086352d8c424d634c7039551f0a5025dc) by [@sangonz193](https://github.com/sangonz193))
|
||||
- Fix text not taking full width ([550b0c0ed1](https://github.com/facebook/react-native/commit/550b0c0ed16a64ce58102f15d4657abe92aac71c) by [@s77rt](https://github.com/s77rt))
|
||||
- Linear gradient start and end point algorithm. ([221d1eceda](https://github.com/facebook/react-native/commit/221d1eceda0e5ab870e96dcdd26e22ab17a3870c) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
|
||||
- Codegen: Support nested objects in arrays ([13780126d3](https://github.com/facebook/react-native/commit/13780126d3cb17ad00c2954f9830a3623812dc3e) by [@tvanlaerhoven](https://github.com/tvanlaerhoven))
|
||||
- RN DevTools: Don't assume 10.0.2.2 is an alias for localhost unless it's used to establish a connection to the server ([69400be4fc](https://github.com/facebook/react-native/commit/69400be4fc0abd38072f6586f3e7d5b6c33e54be) by [@robhogan](https://github.com/robhogan))
|
||||
- Do not discard props in the patch when they are not null while using `useNativeProps` ([4c3112c8d8](https://github.com/facebook/react-native/commit/4c3112c8d8685d6c34be9acf07b18871b3cee5b2) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- : dev-middleware: Remove URL.canParse, restore compat with Node < 18.17 ([99767d43b0](https://github.com/facebook/react-native/commit/99767d43b04e41c83e3bcbfebe267d6fdc284549) by [@robhogan](https://github.com/robhogan))
|
||||
- RN DevTools: Fix fetching sources and source maps when the dev-server is remote and not tunnelled via the same port+protocol. ([d1b0e9a30b](https://github.com/facebook/react-native/commit/d1b0e9a30b3afc56b9355453cf7f6e690bdd8aff) by [@robhogan](https://github.com/robhogan))
|
||||
- Fix npm react-native start when cli-server-api isn't installed ([e0be2efe4e](https://github.com/facebook/react-native/commit/e0be2efe4e80edf99f96a2ae6a25856f6df5e0ca) by [@blakef](https://github.com/blakef))
|
||||
- AnsiHighlight style in RTL layout ([9a3958a619](https://github.com/facebook/react-native/commit/9a3958a619fddb75ed3c6eddebd94f11c0d00e9f) by [@hexboy](https://github.com/hexboy))
|
||||
- Fixes typo in ReactHostImpl ([e3f03269c5](https://github.com/facebook/react-native/commit/e3f03269c5cea28977e44ddf5c5125968336b0e6) by [@rozele](https://github.com/rozele))
|
||||
- TextTransform: capitalize better reflects the web behaviour ([dc2000c875](https://github.com/facebook/react-native/commit/dc2000c8750f42b2f01bdad75455750814d567c6) by [@javache](https://github.com/javache))
|
||||
- Fix for nodes with `display: contents` not being cleaned in some cases ([a88ddcecc9](https://github.com/facebook/react-native/commit/a88ddcecc93a9fa2d231907439d02b11bac8a944) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
- Microtasks are now correctly executed after the code evaluation in Console panel of DevTools. ([3dfe22bd27](https://github.com/facebook/react-native/commit/3dfe22bd27429a43b4648c597b71f7965f31ca65) by [@hoxyq](https://github.com/hoxyq))
|
||||
- Passed height and width as native props to support cases where source is an array. ([45b177f50d](https://github.com/facebook/react-native/commit/45b177f50de624eefcb66bb2d8bc1ffb00855863) by [@shubhamguptadream11](https://github.com/shubhamguptadream11))
|
||||
- `PerformanceEntryReporter::reportMark` and `PerformanceEntryReporter::reportMeasaure` now return created performance entries. ([32f7b3b4e0](https://github.com/facebook/react-native/commit/32f7b3b4e0b8be1d1138f43c46b3c86d9a64c29a) by [@robik](https://github.com/robik))
|
||||
- Make Codegen work with local modules ([7b6e8e7765](https://github.com/facebook/react-native/commit/7b6e8e776574e683821133f0c814969d74c4de61) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Fix KeyboardAvoidingView not aware of the keyboard closing it is unmounted ([08bd8ac47d](https://github.com/facebook/react-native/commit/08bd8ac47da60121225e7b281bbf566e2c5a291e) by [@QichenZhu](https://github.com/QichenZhu))
|
||||
- Fix onEndReached not being called when getItemLayout is present and we scroll past render window ([3485e9ed87](https://github.com/facebook/react-native/commit/3485e9ed871886b3e7408f90d623da5c018da493) by YunPeng Chong)
|
||||
- Fix cast and control paths errors on windows ([0794fa909b](https://github.com/facebook/react-native/commit/0794fa909b2e06fad5c40dce402c5f14a24bb946) by [@TatianaKapos](https://github.com/TatianaKapos))
|
||||
- Plumbing to get boxSizing prop to Yoga round 2 ([3ca796edc3](https://github.com/facebook/react-native/commit/3ca796edc327c5287533dcc8f4394033d5398c2d) by [@joevilches](https://github.com/joevilches))
|
||||
- Fix onEndReached not being called when getItemLayout is present and we scroll past render window ([62b7396bf4](https://github.com/facebook/react-native/commit/62b7396bf45c04bd47e74fe4c1e5b5e59918244d) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- TurboModule::get is now a final method, override `create` to customize property lookup ([5b5e150eaf](https://github.com/facebook/react-native/commit/5b5e150eaff015540720225b9e61acb306e1d107) by [@javache](https://github.com/javache))
|
||||
- Improved types for AnimatedProps ([390925ea39](https://github.com/facebook/react-native/commit/390925ea39eb469768f21cf7069b8f75ccdec09d) by [@javache](https://github.com/javache))
|
||||
- Upgrade Codegen dependency `jscodeshift@17.0.0` to resolve outdated dependencies ([39c98fb8f8](https://github.com/facebook/react-native/commit/39c98fb8f8af98aa40dc89a1580d6c1901fa86cf) by [@byCedric](https://github.com/byCedric))
|
||||
- Fixed issues with W3C PointerEvents testsx ([1dcaf823f5](https://github.com/facebook/react-native/commit/1dcaf823f5e7d9b114dd803ce3181aa0b8f827ad) by [@rozele](https://github.com/rozele))
|
||||
- Fixed issue with W3C PointerEvents tests ([68a6b69b27](https://github.com/facebook/react-native/commit/68a6b69b27275998b4083797942f3e26a92d3adb) by [@rozele](https://github.com/rozele))
|
||||
- Fix setImmediate/clearTimeout mismatch in NativeAnimatedHelper that could clear an unrelated setTimeout. ([a5dd1be889](https://github.com/facebook/react-native/commit/a5dd1be889be21f8daefbc609702989ec7c156cf) by Benoit Girard)
|
||||
- Improved error message when no view config is found. ([bca232ad90](https://github.com/facebook/react-native/commit/bca232ad90692da7a87be5e37ee2680380f94bef) by [@javache](https://github.com/javache))
|
||||
- : Order of operations related to platformConfig propagation in NativeAnimated ([a64183b0c6](https://github.com/facebook/react-native/commit/a64183b0c6a56e9d482c7b8b0f80965493ed87af) by [@rozele](https://github.com/rozele))
|
||||
- Rename overlayColor prop in Modal to backdropColor ([7aeff18970](https://github.com/facebook/react-native/commit/7aeff18970a2b47cbb3fffc1408e4bb21eec6fed) by [@alanleedev](https://github.com/alanleedev))
|
||||
- Fix source mapping for codegenNativeCommands ([8fba154b66](https://github.com/facebook/react-native/commit/8fba154b6655b5d87609d7c9f136997141ea5e99) by [@vzaidman](https://github.com/vzaidman))
|
||||
- Fixed accuracy of FlatList estimations to determine what elements are visible in the rendering window. ([40aaeb7181](https://github.com/facebook/react-native/commit/40aaeb71814a1987482d97fd3170af0add55bc6a) by [@rubennorte](https://github.com/rubennorte))
|
||||
- Fix type conversion error in react native windows build. ([13db1cb88b](https://github.com/facebook/react-native/commit/13db1cb88bdd454cf19be358d6f0cd62f5a6d0cc) by [@marlenecota](https://github.com/marlenecota))
|
||||
- Correctly pass down isLooping in parallel animation ([4014aa4528](https://github.com/facebook/react-native/commit/4014aa4528d43e905246850c83007a635938d7cb) by [@zeyap](https://github.com/zeyap))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Yoga: Fix YogaConfig getting garbage collected #1678 ([7dcb10b6e7](https://github.com/facebook/react-native/commit/7dcb10b6e7e226034a496eaed5351601ae0a1ae2) by [@michaeltroger](https://github.com/michaeltroger))
|
||||
- Enable mix-blend-mode on ReactRootView so blending works with app background ([24b0ded3cf](https://github.com/facebook/react-native/commit/24b0ded3cf715bb0e849d9f7ca8cd2d4d42cbf90) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- RN DevTools: Fix source loading when using an Android emulator connecting to a dev server on the host. ([ca9c56329f](https://github.com/facebook/react-native/commit/ca9c56329fe548b6631934afee0c9be63e1752a1) by [@robhogan](https://github.com/robhogan))
|
||||
- Fixes some deadlocks when doing commits and state updates synchronously from the UI thread (e.g.: from reanimated). ([3986eefed1](https://github.com/facebook/react-native/commit/3986eefed1733f305db5410737a96b635f0159e7) by [@rubennorte](https://github.com/rubennorte))
|
||||
- Ensure setSelection in onAttachedToWindow is within text range ([08759121cd](https://github.com/facebook/react-native/commit/08759121cda3785e4d7ee1f1c82011a3e3a955a9) by [@zeyap](https://github.com/zeyap))
|
||||
- Fixed crash in legacy ReactFontManager ([3da23f7093](https://github.com/facebook/react-native/commit/3da23f7093730e10aeda6b58f4934a381e226dd2) by [@javache](https://github.com/javache))
|
||||
- Dispatch onMomentumScrollEnd after programmatic scrolling ([c69e330324](https://github.com/facebook/react-native/commit/c69e330324724a1e363f4786b2b03ee7ff4b35c5) by [@Biki-das](https://github.com/Biki-das))
|
||||
- RNGP - Do not attempt to substring to 1024 while logging ([e64513bf4e](https://github.com/facebook/react-native/commit/e64513bf4ecce60aff5c04e3bed91b203429d12f) by [@cortinico](https://github.com/cortinico))
|
||||
- Missing isInvertColorsEnabled implementation for Android ([cc1d2853fb](https://github.com/facebook/react-native/commit/cc1d2853fb2b64adfb884cb30c8d22ce0260be15) by [@oddlyspaced](https://github.com/oddlyspaced))
|
||||
- Addressed race condition in surface start. ([6ba7cb3102](https://github.com/facebook/react-native/commit/6ba7cb310273a0ecce6a168bf3aae45fb85bf955) by [@javache](https://github.com/javache))
|
||||
- The definition of ts of resizeMethod attribute is none. ([758892a7d8](https://github.com/facebook/react-native/commit/758892a7d89cc1e316984f8f522738021410f530) by [@nianxiongdi](https://github.com/nianxiongdi))
|
||||
- Reimplement Android lineHeight positioning/determination ([41265bac6b](https://github.com/facebook/react-native/commit/41265bac6b83ee0bb92d645ace81e314065bf5e0) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Fix legacy arch RTL horizontal ScrollView regression ([bfca7cfe7a](https://github.com/facebook/react-native/commit/bfca7cfe7a8e0da10fdb776100fbc706233c0d8d) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Fix RTL ScrollView position when content smaller than container ([0df59d4f03](https://github.com/facebook/react-native/commit/0df59d4f03f79c0fcb9ede2d21bf4d49c97c21ef) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Avoid null reference exception in bridgeless ReactDelegate ([0d664100bb](https://github.com/facebook/react-native/commit/0d664100bbf608550a5c42a4e9b02c6c5bd0f5b8) by [@rozele](https://github.com/rozele))
|
||||
- Fix generating empty line at the end of multiline text view when `textAlign` is set to `justify` ([08e8f6adfd](https://github.com/facebook/react-native/commit/08e8f6adfdcdfec59002f34bb7acf35bb842b331) by [@coado](https://github.com/coado))
|
||||
- Fix Frame Callback not being called after Host Resume ([e8f8ee3c0f](https://github.com/facebook/react-native/commit/e8f8ee3c0f28b8c5751ffaef495adea0b8daf1eb) by [@s77rt](https://github.com/s77rt))
|
||||
- Fix crash in getViewState when using suspense fallbacks. ([bd133b5dd5](https://github.com/facebook/react-native/commit/bd133b5dd57b18140eae51c6d7aaab02874455c1) by [@javache](https://github.com/javache))
|
||||
- Text without explicit font styles was potentially cut-off. ([95a5d1c628](https://github.com/facebook/react-native/commit/95a5d1c6287c7301265207d1962ba8b50d178b20) by [@javache](https://github.com/javache))
|
||||
- Merge Android ViewNativeComponent ViewConfig into BaseViewConfig ([0ba00fc998](https://github.com/facebook/react-native/commit/0ba00fc99891a333d3625db15d9dd832ca7eac88) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Fixed styling on alert dialog titles to wrap two lines and retain bold appearance ([c54b23ff9e](https://github.com/facebook/react-native/commit/c54b23ff9ed7a6bfbb52c081c5afe4b3911d0dd2) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Prevent ArrayIndexOutOfBoundsException in permission check ([6aeca53b3e](https://github.com/facebook/react-native/commit/6aeca53b3ed4530e57a31d7a5593ae16b550b985) by [@antFrancon](https://github.com/antFrancon))
|
||||
- Account for items dynamically scaling with the container when using `maintainVisibleContentPosition` in virtualized lists ([6c19996e10](https://github.com/facebook/react-native/commit/6c19996e10eda97a3501f53a68da246d1f122d01) by [@fabriziocucci](https://github.com/fabriziocucci))
|
||||
- Apps will no longer fatally crash when trying to draw large images ([483b928224](https://github.com/facebook/react-native/commit/483b92822496fa2e6339f75049a33be1e9567f52) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Add missing BaseViewManager props to BaseViewManagerDelegate ([6741fd94ad](https://github.com/facebook/react-native/commit/6741fd94ad26012938caa65a5129f0eca6f2246e) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Fix TextInput caret moving to the beginning when attached to window ([ca0abd1b9e](https://github.com/facebook/react-native/commit/ca0abd1b9ea164a566b39cb9090eaa75647aad5a) by [@QichenZhu](https://github.com/QichenZhu))
|
||||
- Fix issue where `onDropViewInstance` cleanup was not being handled after `ReactRootView.unmountReactApplication` ([0449630612](https://github.com/facebook/react-native/commit/04496306123c46730b46accb2cd6239531e51fef) by [@rozele](https://github.com/rozele))
|
||||
- Fix issues with Modals and lifecycle events in multi-surface apps ([1ffef5669c](https://github.com/facebook/react-native/commit/1ffef5669c21f4b2c5fec6bc58a85f95518cf10e) by [@rozele](https://github.com/rozele))
|
||||
- Fix mising `ANTI_ALIAS_FLAG` when resetting Text Paint ([276e3a7df7](https://github.com/facebook/react-native/commit/276e3a7df7de1b04dec0891fa7ed72917568ee7b) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Fix some cases where we override setBackgroundColor on View-level instead of VM level ([0b0ac81fbe](https://github.com/facebook/react-native/commit/0b0ac81fbeebf819e744b6ecc19d549aaab8406d) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Avoid blocking the main thread when decompressing drawable resources ([420229d669](https://github.com/facebook/react-native/commit/420229d66946320f06485c5a3d3c167eae1a407e) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Reenable `setAndroidLayoutDirection` by default ([6cf0cfb5a4](https://github.com/facebook/react-native/commit/6cf0cfb5a47a437b8a17b50b4c70460be15ee1cd) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Properly fix measurement of trailing newlines ([060c594457](https://github.com/facebook/react-native/commit/060c59445740f9b92188d2193623b36c3d859ba7) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Fix crash for Modal not attached to window manager ([eaa780de1c](https://github.com/facebook/react-native/commit/eaa780de1c799bf35fded2914d27b1953b093340) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Fix NPE on ReactTextInputManager.setTextDecorationLine ([41c6ad5597](https://github.com/facebook/react-native/commit/41c6ad55978184cad1a32ec63c2d1f07e8282d5e) by [@cortinico](https://github.com/cortinico))
|
||||
- BoxShadow now supports platformColor. ([4ede9205a0](https://github.com/facebook/react-native/commit/4ede9205a0559fcaf7ac58a3d70bc902eb23fcb6) by [@javache](https://github.com/javache))
|
||||
- MixBlendMode now properly does state updates ([fae572d815](https://github.com/facebook/react-native/commit/fae572d815c7fb9684fc6c3b7f001de35ea8fb69) by [@jorge-cab](https://github.com/jorge-cab))
|
||||
- Fix interactions between removeClippedSubviews and RTL ([513e9669e7](https://github.com/facebook/react-native/commit/513e9669e78a4bfd9b0380335c61f581343c4009) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- LayoutAnimations work on full new architecture ([43af902693](https://github.com/facebook/react-native/commit/43af902693f0befde802a7f684e26b19ec7126c8) by [@javache](https://github.com/javache))
|
||||
- Fix: ReactDelegate/ReactFragment crashing on New Architecture apps ([12dda31bc1](https://github.com/facebook/react-native/commit/12dda31bc12f4a04ec9df70d30328b1001bb2cec) by [@cortinico](https://github.com/cortinico))
|
||||
- ReactFragment should properly instantiate ReactDelegate on Bridgeless ([7176d11ce4](https://github.com/facebook/react-native/commit/7176d11ce468ca37e9cbe8afcc1f6344c250dd0c) by [@cortinico](https://github.com/cortinico))
|
||||
- Fixed text being measured incorrectly when ending with an empty line on the new architecture ([bd323929dc](https://github.com/facebook/react-native/commit/bd323929dc5be5666ee36043babec7d981a095dc) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
- Linear gradient with platform colors ([6866968a79](https://github.com/facebook/react-native/commit/6866968a79d44518884965959760983acdadc17c) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
|
||||
- ARG_DISABLE_HOST_LIFECYCLE_EVENTS in ReactFragment to allow unmounting a surface without destroying ReactHost. ([40c875deca](https://github.com/facebook/react-native/commit/40c875decabaa56d6dd45ce03b22e8b3931781ac) by [@vincenzovitale](https://github.com/vincenzovitale))
|
||||
- Fixed incorrect scroll event/position for scroll views when doing a smooth scroll animation. ([b4c41ec768](https://github.com/facebook/react-native/commit/b4c41ec7685da6e8ef35c41c30aa402639bc42c1) by [@rubennorte](https://github.com/rubennorte))
|
||||
- Use appropriate Nullable attribute for ReactRootView field in ReactDelegate ([cbddcfc691](https://github.com/facebook/react-native/commit/cbddcfc6911101608df36f2a8d047768296c63b2) by [@rozele](https://github.com/rozele))
|
||||
- Hover events were dispatched incorrectly when multiple ReactRoots were layered. ([533ef2ca37](https://github.com/facebook/react-native/commit/533ef2ca37d3b19fc909315f183e3f30dbf4b93d) by [@javache](https://github.com/javache))
|
||||
- Improve text line height calculation ([65d8f66b50](https://github.com/facebook/react-native/commit/65d8f66b50471d2fb4ddd5e63e17fcc808623110) by [@mellyeliu](https://github.com/mellyeliu))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Fix possible NSRangeException when updating typing attributes in response to new text content ([6e06a810f0](https://github.com/facebook/react-native/commit/6e06a810f0ab1002f1d10351bc878babde36b1f1) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- Ensure artifacts directory exists when building Hermes from local source ([c3091ccfed](https://github.com/facebook/react-native/commit/c3091ccfed4440d7f0b906a7eb954718656b71b2) by [@kraenhansen](https://github.com/kraenhansen))
|
||||
- "Reconnect DevTools" button not working sometimes ([8507204b53](https://github.com/facebook/react-native/commit/8507204b533cf87d1e4345a9c062cd10cf0022c1) by [@EdmondChuiHW](https://github.com/EdmondChuiHW))
|
||||
- Fixed use of view commands from layout effects ([6f1c2a512e](https://github.com/facebook/react-native/commit/6f1c2a512e44d25edefea53e864f688018745c07) by [@sammy-SC](https://github.com/sammy-SC))
|
||||
- Fix `r` & `d` not working from Metro sometimes ([9a60038a40](https://github.com/facebook/react-native/commit/9a60038a40e16925ea1adeb3e3c937c22a615485) by [@EdmondChuiHW](https://github.com/EdmondChuiHW))
|
||||
- Resolve deprecated function prototype warning in RCTAppearance.h ([594c9d9a46](https://github.com/facebook/react-native/commit/594c9d9a46623016abb6bbd0d8f2982bf68bdc34) by [@eli-front](https://github.com/eli-front))
|
||||
- Fixes rn-tester bundled images examples ([8cbc7c3357](https://github.com/facebook/react-native/commit/8cbc7c3357ac4043e6c423261c090a1bf656f68e) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- Fixed `onMomentumScrollBegin` event not firing on command-driven scroll events ([5b609cca09](https://github.com/facebook/react-native/commit/5b609cca099b3b0d4fc66d4cf50f69d3c5b7fc8e) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- Typo in spm.rb ([5e18f7f788](https://github.com/facebook/react-native/commit/5e18f7f788ccbea60e96b8e7deab29d423ccf1a6) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Add png extension only if file exist when load local image ([44f2a08371](https://github.com/facebook/react-native/commit/44f2a083716205b7560bef2f09d25830e29cd0c5) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- FIxes CornerRadiiAreEqualAndSymmetrical error when check topLeftHorizontal == topLeftVertical ([af384a914a](https://github.com/facebook/react-native/commit/af384a914a4e9ef6a5d25b00bc14b0483e5af879) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- Fix numerous class interfaces having incorrect designated initializer patterns ([b98846c2e3](https://github.com/facebook/react-native/commit/b98846c2e37c3ec895536ab942978d7701f51a5e) by Nolan O'Brien)
|
||||
- AutomaticallyAdjustKeyboardInsets not shifting scrollview content ([2d9933e616](https://github.com/facebook/react-native/commit/2d9933e616c3efe57ed0ca141277638182a69d9c) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- Fix launching App Clips with nullish URLs. ([043e2fe14a](https://github.com/facebook/react-native/commit/043e2fe14a6f13885a552211f17d61292001fa76) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Fixes scrollIndicatorInsets not work in old arch ([c1178ac208](https://github.com/facebook/react-native/commit/c1178ac2085e073afb07453eefc9095a1f5d530f) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- Silenced 'set but unused variable' warning. ([f7b28e86aa](https://github.com/facebook/react-native/commit/f7b28e86aaded58f76978179d8d83f11e913b026) by [@nlutsenko](https://github.com/nlutsenko))
|
||||
- Fixed crash on promise rejection handler in iOS 18. ([26d8d490e4](https://github.com/facebook/react-native/commit/26d8d490e47bd3fa46dc4a3b8e66e9ec35c15cf7) by David Rickard)
|
||||
- Fixed scroll view ([ab8f3ff3e9](https://github.com/facebook/react-native/commit/ab8f3ff3e9e6445fa9961467e965792908d4c2bd) by [@coado](https://github.com/coado))
|
||||
- Fixes missing char mode of linebreakmode ([77889afa1c](https://github.com/facebook/react-native/commit/77889afa1c9d0bdb966102860a5d1d1d91187ae8) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- Removed usage of set but unused variable. ([d386dfc804](https://github.com/facebook/react-native/commit/d386dfc804842ed36565966da5aed15d9df7960e) by [@nlutsenko](https://github.com/nlutsenko))
|
||||
- Cast the UIScene to UIWindowScene only if the scene respond to the selector ([fdee0ebbcb](https://github.com/facebook/react-native/commit/fdee0ebbcb88f40c23b92b07792fbb9d1041c546) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Move RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED to const ([a1ec345e2a](https://github.com/facebook/react-native/commit/a1ec345e2a2cdb4ccf255293616ed5e90f519e40) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Fixes the exported synchronous method not being called on the method queue when it's the main queue ([8bfd7e1039](https://github.com/facebook/react-native/commit/8bfd7e10393e649554c7246df430019c4f78d5e0) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- Make sure that the Increment and Decrement accessibility actions works on iOS ([303e0ed764](https://github.com/facebook/react-native/commit/303e0ed7641409acf2d852c077f6be426afd7a0c) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Subscribe to orientation changes in RCTUIManager only on iOS ([2f5adc3874](https://github.com/facebook/react-native/commit/2f5adc3874bcdfc8d7d2c3cd5293eefec63a91f8) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Fix ActionSheetIOS crash `attempt to insert nil object from objects` ([bebd6531b5](https://github.com/facebook/react-native/commit/bebd6531b5f4ef0300aa5a4b6f9d23e60b57a25e) by [@RodolfoGS](https://github.com/RodolfoGS))
|
||||
- Allow pods mixte type settings on post-install ([1e59f2e3f8](https://github.com/facebook/react-native/commit/1e59f2e3f8f0ab3ee4173bddaa089bbecf61d1eb) by [@MasGaNo](https://github.com/MasGaNo))
|
||||
- Fallback to old resolve mechanism when node require fails to resolve react native path ([3cbaddbc16](https://github.com/facebook/react-native/commit/3cbaddbc164b9dc326b2a7ddc0b35ce885bc7d9d) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- Fixed warnings when validating SVC ([de39a204c3](https://github.com/facebook/react-native/commit/de39a204c3588a3c02dc2e72464174c75b3a6749) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Solved SVC warnings for RNTester ([fad4a0783b](https://github.com/facebook/react-native/commit/fad4a0783b0a0478c147d9bde2ef9ab082a08297) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- Don't reference PrivacyInfo.xcprivacy twice for new projects ([cadd41b1a2](https://github.com/facebook/react-native/commit/cadd41b1a2e16b1c77a8d3022f4ccbdbd5ea295f) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
|
||||
## v0.76.3
|
||||
|
||||
### Fixed
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Look for sdkmanager.bat ([6460d2b3e7](https://github.com/facebook/react-native/commit/6460d2b3e74c3a6e4075e9f12304c16427c3b1b2) by [@blakef](https://github.com/blakef))
|
||||
- CMake Windows path normalization ([9946838bedd](https://github.com/facebook/react-native/commit/9946838beddac549053a3ba3eb3b1b49c22a3fd9) by [@blakef](https://github.com/blakef))
|
||||
- Fix build failure on windows in android ([08b83005481](https://github.com/facebook/react-native/commit/08b830054812f4011becbbacdf1525701225df39) by [@FouadMagdy01](https://github.com/FouadMagdy01))
|
||||
|
||||
## v0.76.2
|
||||
|
||||
### Added
|
||||
|
||||
@@ -87,7 +87,7 @@ The full documentation for React Native can be found on our [website][docs].
|
||||
|
||||
The React Native documentation discusses components, APIs, and topics that are specific to React Native. For further documentation on the React API that is shared between React Native and React DOM, refer to the [React documentation][r-docs].
|
||||
|
||||
The source for the React Native documentation and website is hosted on a separate repo, [**@facebook/react-native-website**][repo-website].
|
||||
The source for the React Native documentation and website is hosted on a separate repository, [**@facebook/react-native-website**][repo-website].
|
||||
|
||||
[docs]: https://reactnative.dev/docs/getting-started
|
||||
[r-docs]: https://react.dev/learn
|
||||
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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 'deep-equal' {
|
||||
declare module.exports: (
|
||||
actual: mixed,
|
||||
expected: mixed,
|
||||
options?: {strict: boolean},
|
||||
) => boolean;
|
||||
}
|
||||
Vendored
-39
@@ -1,39 +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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
declare module '@pkgjs/parseargs' {
|
||||
declare export function parseArgs<
|
||||
TOptions: {[string]: util$ParseArgsOption} = {},
|
||||
>(config: {
|
||||
args?: Array<string>,
|
||||
options?: TOptions,
|
||||
strict?: boolean,
|
||||
allowPositionals?: boolean,
|
||||
tokens?: false,
|
||||
}): {
|
||||
values: util$ParseArgsOptionsToValues<TOptions>,
|
||||
positionals: Array<string>,
|
||||
};
|
||||
|
||||
declare export function parseArgs<
|
||||
TOptions: {[string]: util$ParseArgsOption} = {},
|
||||
>(config: {
|
||||
args?: Array<string>,
|
||||
options?: TOptions,
|
||||
strict?: boolean,
|
||||
allowPositionals?: boolean,
|
||||
tokens: true,
|
||||
}): {
|
||||
values: util$ParseArgsOptionsToValues<TOptions>,
|
||||
positionals: Array<string>,
|
||||
tokens: Array<util$ParseArgsToken>,
|
||||
};
|
||||
}
|
||||
Vendored
+20
@@ -16,6 +16,13 @@ declare interface undici$Agent$Options {
|
||||
}
|
||||
|
||||
declare module 'undici' {
|
||||
declare export type RequestOptions = $ReadOnly<{
|
||||
dispatcher?: Dispatcher,
|
||||
method?: string,
|
||||
headers?: HeadersInit,
|
||||
...
|
||||
}>;
|
||||
|
||||
declare export class Dispatcher extends events$EventEmitter {
|
||||
constructor(): void;
|
||||
}
|
||||
@@ -23,4 +30,17 @@ declare module 'undici' {
|
||||
declare export class Agent extends Dispatcher {
|
||||
constructor(opts?: undici$Agent$Options): void;
|
||||
}
|
||||
|
||||
declare export function request(
|
||||
url: string | URL,
|
||||
options: RequestOptions,
|
||||
): Promise<{
|
||||
statusCode: number,
|
||||
headers: Headers,
|
||||
body: {
|
||||
read(): Promise<Buffer>,
|
||||
...
|
||||
},
|
||||
...
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
|
||||
module.exports = {
|
||||
getHasteName(filePath) {
|
||||
if (filePath.endsWith('ReactNativeInternalFeatureFlags.js')) {
|
||||
return 'ReactNativeInternalFeatureFlags';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
getCacheKey() {
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update(fs.readFileSync(__filename))
|
||||
.digest('hex');
|
||||
},
|
||||
};
|
||||
@@ -18,6 +18,7 @@ module.exports = {
|
||||
'<rootDir>/packages/react-native',
|
||||
'<rootDir>/jest/integration/runtime',
|
||||
],
|
||||
moduleFileExtensions: [...baseConfig.moduleFileExtensions, 'cpp', 'h'],
|
||||
// This allows running Meta-internal tests with the `-test.fb.js` suffix.
|
||||
testRegex: '/__tests__/.*-itest(\\.fb)?\\.js$',
|
||||
testPathIgnorePatterns: baseConfig.testPathIgnorePatterns,
|
||||
|
||||
@@ -17,24 +17,37 @@ const rnTesterConfig = getDefaultConfig(
|
||||
path.resolve('../../../packages/rn-tester'),
|
||||
);
|
||||
|
||||
const JS_DIR = process.env.JS_DIR
|
||||
? path.resolve(process.cwd(), process.env.JS_DIR)
|
||||
: null;
|
||||
|
||||
const config = {
|
||||
projectRoot: path.resolve(__dirname, '../../..'),
|
||||
reporter: {
|
||||
update: () => {},
|
||||
},
|
||||
resolver: {
|
||||
blockList: null,
|
||||
sourceExts: [...rnTesterConfig.resolver.sourceExts, 'fb.js'],
|
||||
nodeModulesPaths: process.env.JS_DIR
|
||||
? [path.join(process.env.JS_DIR, 'public', 'node_modules')]
|
||||
blockList: /\/RendererProxy\.fb\.js$/, // Disable dependency injection for the renderer
|
||||
disableHierarchicalLookup: !!JS_DIR,
|
||||
sourceExts: ['fb.js', ...rnTesterConfig.resolver.sourceExts],
|
||||
nodeModulesPaths: JS_DIR
|
||||
? [path.join(JS_DIR, 'public', 'node_modules')]
|
||||
: [],
|
||||
hasteImplModulePath: path.resolve(__dirname, 'hasteImpl.js'),
|
||||
},
|
||||
transformer: {
|
||||
// We need to wrap the default transformer so we can run it from source
|
||||
// using babel-register.
|
||||
babelTransformerPath: path.resolve(__dirname, 'metro-babel-transformer.js'),
|
||||
},
|
||||
watchFolders: process.env.JS_DIR ? [process.env.JS_DIR] : [],
|
||||
watchFolders: JS_DIR
|
||||
? [
|
||||
path.join(JS_DIR, 'RKJSModules', 'vendor', 'react'),
|
||||
path.join(JS_DIR, 'tools', 'metro'),
|
||||
path.join(JS_DIR, 'node_modules'),
|
||||
path.join(JS_DIR, 'public', 'node_modules'),
|
||||
]
|
||||
: [],
|
||||
};
|
||||
|
||||
module.exports = mergeConfig(rnTesterConfig, config);
|
||||
|
||||
@@ -24,6 +24,9 @@ import path from 'path';
|
||||
|
||||
const BUILD_OUTPUT_PATH = path.resolve(__dirname, '..', 'build');
|
||||
|
||||
const ENABLE_OPTIMIZED_MODE: false = false;
|
||||
const PRINT_FANTOM_OUTPUT: false = false;
|
||||
|
||||
function parseRNTesterCommandResult(
|
||||
commandArgs: $ReadOnlyArray<string>,
|
||||
result: ReturnType<typeof spawnSync>,
|
||||
@@ -58,15 +61,17 @@ function parseRNTesterCommandResult(
|
||||
}
|
||||
|
||||
function getBuckModeForPlatform() {
|
||||
const mode = ENABLE_OPTIMIZED_MODE ? 'opt' : 'dev';
|
||||
|
||||
switch (os.platform()) {
|
||||
case 'linux':
|
||||
return '@//arvr/mode/linux/dev';
|
||||
return `@//arvr/mode/linux/${mode}`;
|
||||
case 'darwin':
|
||||
return os.arch() === 'arm64'
|
||||
? '@//arvr/mode/mac-arm/dev'
|
||||
: '@//arvr/mode/mac/dev';
|
||||
? `@//arvr/mode/mac-arm/${mode}`
|
||||
: `@//arvr/mode/mac/${mode}`;
|
||||
case 'win32':
|
||||
return '@//arvr/mode/win/dev';
|
||||
return `@//arvr/mode/win/${mode}`;
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${os.platform()}`);
|
||||
}
|
||||
@@ -76,6 +81,55 @@ function getShortHash(contents: string): string {
|
||||
return crypto.createHash('md5').update(contents).digest('hex').slice(0, 8);
|
||||
}
|
||||
|
||||
function generateBytecodeBundle({
|
||||
sourcePath,
|
||||
bytecodePath,
|
||||
}: {
|
||||
sourcePath: string,
|
||||
bytecodePath: string,
|
||||
}): void {
|
||||
const hermesCompilerCommandArgs = [
|
||||
'run',
|
||||
getBuckModeForPlatform(),
|
||||
'//xplat/hermes/tools/hermesc:hermesc',
|
||||
'--',
|
||||
'-emit-binary',
|
||||
'-O',
|
||||
'-max-diagnostic-width',
|
||||
'80',
|
||||
'-out',
|
||||
bytecodePath,
|
||||
sourcePath,
|
||||
];
|
||||
|
||||
const hermesCompilerCommandResult = spawnSync(
|
||||
'buck2',
|
||||
hermesCompilerCommandArgs,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: `/usr/local/bin:${process.env.PATH ?? ''}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (hermesCompilerCommandResult.status !== 0) {
|
||||
throw new Error(
|
||||
[
|
||||
'Failed to run Hermes compiler. Full output:',
|
||||
'buck2 ' + hermesCompilerCommandArgs.join(' '),
|
||||
'stdout:',
|
||||
hermesCompilerCommandResult.stdout,
|
||||
'stderr:',
|
||||
hermesCompilerCommandResult.stderr,
|
||||
'error:',
|
||||
hermesCompilerCommandResult.error,
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = async function runTest(
|
||||
globalConfig: {...},
|
||||
config: {...},
|
||||
@@ -85,6 +139,8 @@ module.exports = async function runTest(
|
||||
): mixed {
|
||||
const startTime = Date.now();
|
||||
|
||||
const isOptimizedMode = ENABLE_OPTIMIZED_MODE;
|
||||
|
||||
const metroConfig = await Metro.loadConfig({
|
||||
config: path.resolve(__dirname, '..', 'config', 'metro.config.js'),
|
||||
});
|
||||
@@ -92,8 +148,8 @@ module.exports = async function runTest(
|
||||
const setupModulePath = path.resolve(__dirname, '../runtime/setup.js');
|
||||
|
||||
const entrypointContents = entrypointTemplate({
|
||||
testPath: `.${path.sep}${path.relative(BUILD_OUTPUT_PATH, testPath)}`,
|
||||
setupModulePath: `.${path.sep}${path.relative(BUILD_OUTPUT_PATH, setupModulePath)}`,
|
||||
testPath: `${path.relative(BUILD_OUTPUT_PATH, testPath)}`,
|
||||
setupModulePath: `${path.relative(BUILD_OUTPUT_PATH, setupModulePath)}`,
|
||||
});
|
||||
|
||||
const entrypointPath = path.join(
|
||||
@@ -101,24 +157,34 @@ module.exports = async function runTest(
|
||||
`${getShortHash(entrypointContents)}-${path.basename(testPath)}`,
|
||||
);
|
||||
const testBundlePath = entrypointPath + '.bundle';
|
||||
const testJSBundlePath = testBundlePath + '.js';
|
||||
const testBytecodeBundlePath = testJSBundlePath + '.hbc';
|
||||
|
||||
fs.mkdirSync(path.dirname(entrypointPath), {recursive: true});
|
||||
fs.writeFileSync(entrypointPath, entrypointContents, 'utf8');
|
||||
|
||||
await Metro.runBuild(metroConfig, {
|
||||
entry: entrypointPath,
|
||||
out: testBundlePath,
|
||||
out: testJSBundlePath,
|
||||
platform: 'android',
|
||||
minify: false,
|
||||
dev: true,
|
||||
minify: isOptimizedMode,
|
||||
dev: !isOptimizedMode,
|
||||
});
|
||||
|
||||
if (isOptimizedMode) {
|
||||
generateBytecodeBundle({
|
||||
sourcePath: testJSBundlePath,
|
||||
bytecodePath: testBytecodeBundlePath,
|
||||
});
|
||||
}
|
||||
|
||||
const rnTesterCommandArgs = [
|
||||
'run',
|
||||
getBuckModeForPlatform(),
|
||||
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
|
||||
'--',
|
||||
`--bundlePath=${testBundlePath}`,
|
||||
'--bundlePath',
|
||||
testBundlePath,
|
||||
];
|
||||
const rnTesterCommandResult = spawnSync('buck2', rnTesterCommandArgs, {
|
||||
encoding: 'utf8',
|
||||
@@ -143,6 +209,21 @@ module.exports = async function runTest(
|
||||
);
|
||||
}
|
||||
|
||||
if (PRINT_FANTOM_OUTPUT) {
|
||||
console.log(
|
||||
[
|
||||
'RN tester binary. Full output:',
|
||||
'buck2 ' + rnTesterCommandArgs.join(' '),
|
||||
'stdout:',
|
||||
rnTesterCommandResult.stdout,
|
||||
'stderr:',
|
||||
rnTesterCommandResult.stderr,
|
||||
'error:',
|
||||
rnTesterCommandResult.error,
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
const rnTesterParsedOutput = parseRNTesterCommandResult(
|
||||
rnTesterCommandArgs,
|
||||
rnTesterCommandResult,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
enableFabricCompleteRootInCommitPhase: true,
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
export type TestCaseResult = {
|
||||
@@ -114,8 +115,98 @@ global.it.skip =
|
||||
globalModifiers.pop();
|
||||
};
|
||||
|
||||
global.jest = {
|
||||
fn: createMockFunction,
|
||||
};
|
||||
|
||||
const MOCK_FN_TAG = Symbol('mock function');
|
||||
|
||||
function createMockFunction<TArgs: $ReadOnlyArray<mixed>, TReturn>(
|
||||
initialImplementation?: (...TArgs) => TReturn,
|
||||
): JestMockFn<TArgs, TReturn> {
|
||||
let implementation: ?(...TArgs) => TReturn = initialImplementation;
|
||||
|
||||
const mock: JestMockFn<TArgs, TReturn>['mock'] = {
|
||||
calls: [],
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
lastCall: undefined,
|
||||
instances: [],
|
||||
contexts: [],
|
||||
results: [],
|
||||
};
|
||||
|
||||
const mockFunction = function (this: mixed, ...args: TArgs): TReturn {
|
||||
let result: JestMockFn<TArgs, TReturn>['mock']['results'][number] = {
|
||||
isThrow: false,
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
value: undefined,
|
||||
};
|
||||
|
||||
if (implementation != null) {
|
||||
try {
|
||||
result.value = implementation.apply(this, args);
|
||||
} catch (error) {
|
||||
result.isThrow = true;
|
||||
result.value = error;
|
||||
}
|
||||
}
|
||||
|
||||
mock.calls.push(args);
|
||||
mock.lastCall = args;
|
||||
// $FlowExpectedError[incompatible-call]
|
||||
mock.instances.push(new.target ? this : undefined);
|
||||
mock.contexts.push(this);
|
||||
mock.results.push(result);
|
||||
|
||||
if (result.isThrow) {
|
||||
throw result.value;
|
||||
}
|
||||
|
||||
return result.value;
|
||||
};
|
||||
|
||||
mockFunction.mock = mock;
|
||||
// $FlowExpectedError[invalid-computed-prop]
|
||||
mockFunction[MOCK_FN_TAG] = true;
|
||||
|
||||
// $FlowExpectedError[prop-missing]
|
||||
return mockFunction;
|
||||
}
|
||||
|
||||
// flowlint unsafe-getters-setters:off
|
||||
|
||||
class ErrorWithCustomBlame extends Error {
|
||||
// Initially 5 to ignore all the frames from Babel helpers to instantiate this
|
||||
// custom error class.
|
||||
#ignoredFrameCount: number = 5;
|
||||
#cachedProcessedStack: ?string;
|
||||
|
||||
blameToPreviousFrame(): this {
|
||||
this.#ignoredFrameCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
get stack(): string {
|
||||
if (this.#cachedProcessedStack == null) {
|
||||
const originalStack = super.stack;
|
||||
|
||||
if (originalStack == null) {
|
||||
this.#cachedProcessedStack = originalStack;
|
||||
} else {
|
||||
const lines = originalStack.split('\n');
|
||||
lines.splice(1, this.#ignoredFrameCount);
|
||||
this.#cachedProcessedStack = lines.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return this.#cachedProcessedStack;
|
||||
}
|
||||
|
||||
set stack(value: string) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
class Expect {
|
||||
#received: mixed;
|
||||
#isNot: boolean = false;
|
||||
@@ -129,21 +220,30 @@ class Expect {
|
||||
return this;
|
||||
}
|
||||
|
||||
toEqual(expected: mixed): void {
|
||||
const pass = deepEqual(this.#received, expected, {strict: true});
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected${this.#maybeNotLabel()} to equal ${String(expected)} but received ${String(this.#received)}.`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toBe(expected: mixed): void {
|
||||
const pass = this.#received === expected;
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new Error(
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected${this.#maybeNotLabel()} ${String(expected)} but received ${String(this.#received)}.`,
|
||||
);
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toBeInstanceOf(expected: Class<mixed>): void {
|
||||
const pass = this.#received instanceof expected;
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new Error(
|
||||
throw new ErrorWithCustomBlame(
|
||||
`expected ${String(this.#received)}${this.#maybeNotLabel()} to be an instance of ${String(expected)}`,
|
||||
);
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,28 +251,59 @@ class Expect {
|
||||
const pass =
|
||||
Math.abs(expected - Number(this.#received)) < Math.pow(10, -precision);
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new Error(
|
||||
`expected ${String(this.#received)}${this.#maybeNotLabel()} to be close to ${expected}`,
|
||||
);
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to be close to ${expected}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toThrow(error: mixed): void {
|
||||
if (error != null) {
|
||||
throw new Error('toThrow() implementation does not accept arguments.');
|
||||
toBeNull(): void {
|
||||
const pass = this.#received == null;
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to be null`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toThrow(expected?: string): void {
|
||||
if (expected != null && typeof expected !== 'string') {
|
||||
throw new ErrorWithCustomBlame(
|
||||
'toThrow() implementation only accepts strings as arguments.',
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
let pass = false;
|
||||
try {
|
||||
// $FlowExpectedError[not-a-function]
|
||||
this.#received();
|
||||
} catch {
|
||||
pass = true;
|
||||
} catch (error) {
|
||||
pass = expected != null ? error.message === expected : true;
|
||||
}
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new Error(
|
||||
`expected ${String(this.#received)}${this.#maybeNotLabel()} to throw`,
|
||||
);
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to throw`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toHaveBeenCalled(): void {
|
||||
const mock = this.#requireMock();
|
||||
const pass = mock.calls.length > 0;
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to have been called, but it was${this.#isNot ? '' : "n't"}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toHaveBeenCalledTimes(times: number): void {
|
||||
const mock = this.#requireMock();
|
||||
const pass = mock.calls.length === times;
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to have been called ${times} times, but it was called ${mock.calls.length} times`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +314,20 @@ class Expect {
|
||||
#maybeNotLabel(): string {
|
||||
return this.#isNot ? ' not' : '';
|
||||
}
|
||||
|
||||
#requireMock(): JestMockFn<$ReadOnlyArray<mixed>, mixed>['mock'] {
|
||||
// $FlowExpectedError[incompatible-use]
|
||||
if (!this.#received?.[MOCK_FN_TAG]) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)} to be a mock function, but it wasn't`,
|
||||
)
|
||||
.blameToPreviousFrame()
|
||||
.blameToPreviousFrame();
|
||||
}
|
||||
|
||||
// $FlowExpectedError[incompatible-use]
|
||||
return this.#received.mock;
|
||||
}
|
||||
}
|
||||
|
||||
global.expect = (received: mixed) => new Expect(received);
|
||||
@@ -235,7 +380,9 @@ function executeTests() {
|
||||
result.status = status;
|
||||
result.duration = Date.now() - start;
|
||||
result.failureMessages =
|
||||
status === 'failed' && error ? [error.message] : [];
|
||||
status === 'failed' && error
|
||||
? [error.stack ?? error.message ?? String(error)]
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -48,7 +48,6 @@
|
||||
"@babel/preset-flow": "^7.24.7",
|
||||
"@definitelytyped/dtslint": "^0.0.127",
|
||||
"@jest/create-cache-key-function": "^29.6.3",
|
||||
"@pkgjs/parseargs": "^0.11.0",
|
||||
"@react-native/metro-babel-transformer": "0.77.0-main",
|
||||
"@react-native/metro-config": "0.77.0-main",
|
||||
"@tsconfig/node18": "1.0.1",
|
||||
@@ -56,12 +55,13 @@
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"ansi-styles": "^4.2.1",
|
||||
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
|
||||
"babel-plugin-syntax-hermes-parser": "0.24.0",
|
||||
"babel-plugin-syntax-hermes-parser": "0.25.1",
|
||||
"babel-plugin-transform-define": "^2.1.4",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"chalk": "^4.0.0",
|
||||
"clang-format": "^1.8.0",
|
||||
"connect": "^3.6.5",
|
||||
"deep-equal": "1.1.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
@@ -76,11 +76,11 @@
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"eslint-plugin-redundant-undefined": "^0.4.0",
|
||||
"eslint-plugin-relay": "^1.8.3",
|
||||
"flow-api-translator": "0.24.0",
|
||||
"flow-bin": "^0.253.0",
|
||||
"flow-api-translator": "0.25.1",
|
||||
"flow-bin": "^0.255.0",
|
||||
"glob": "^7.1.1",
|
||||
"hermes-eslint": "0.24.0",
|
||||
"hermes-transform": "0.24.0",
|
||||
"hermes-eslint": "0.25.1",
|
||||
"hermes-transform": "0.25.1",
|
||||
"inquirer": "^7.1.0",
|
||||
"jest": "^29.6.3",
|
||||
"jest-junit": "^10.0.0",
|
||||
@@ -92,7 +92,7 @@
|
||||
"node-fetch": "^2.2.0",
|
||||
"nullthrows": "^1.1.1",
|
||||
"prettier": "2.8.8",
|
||||
"prettier-plugin-hermes-parser": "0.24.0",
|
||||
"prettier-plugin-hermes-parser": "0.25.1",
|
||||
"react": "18.3.1",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
"metro": "^0.81.0",
|
||||
"metro-config": "^0.81.0",
|
||||
"metro-core": "^0.81.0",
|
||||
"node-fetch": "^2.2.0",
|
||||
"readline": "^1.3.0",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
import type TerminalReporter from 'metro/src/lib/TerminalReporter';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
type PageDescription = $ReadOnly<{
|
||||
id: string,
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
*/
|
||||
|
||||
import net from 'net';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
/**
|
||||
* Determine whether we can run the dev server.
|
||||
|
||||
@@ -10,24 +10,30 @@
|
||||
*/
|
||||
|
||||
import type {JSONSerializable} from '../inspector-proxy/types';
|
||||
import type {RequestOptions} from 'undici';
|
||||
|
||||
import {Agent} from 'undici';
|
||||
|
||||
declare var globalThis: $FlowFixMe;
|
||||
import {Agent, request} from 'undici';
|
||||
|
||||
/**
|
||||
* A version of `fetch` that is usable with the HTTPS server created in
|
||||
* ServerUtils (which uses a self-signed certificate).
|
||||
*/
|
||||
export async function fetchLocal(
|
||||
export async function requestLocal(
|
||||
url: string,
|
||||
options?: Partial<Parameters<typeof fetch>[1] & {dispatcher?: mixed}>,
|
||||
): ReturnType<typeof fetch> {
|
||||
return await fetch(url, {
|
||||
options?: RequestOptions,
|
||||
): Promise<{
|
||||
statusCode: number,
|
||||
headers: Headers,
|
||||
bodyBuffer: Buffer,
|
||||
}> {
|
||||
const {
|
||||
statusCode,
|
||||
headers: rawHeaders,
|
||||
body,
|
||||
} = await request(url, {
|
||||
...options,
|
||||
// Node's native `fetch` comes from undici and supports the same options,
|
||||
// including `dispatcher` which we use to make it accept self-signed
|
||||
// certificates.
|
||||
|
||||
// Use undici's `dispatcher` to make it accept self-signed certificates.
|
||||
dispatcher:
|
||||
options?.dispatcher ??
|
||||
new Agent({
|
||||
@@ -36,41 +42,48 @@ export async function fetchLocal(
|
||||
},
|
||||
}),
|
||||
});
|
||||
return {
|
||||
statusCode,
|
||||
bodyBuffer: await body.read(),
|
||||
headers: new Headers(rawHeaders),
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchJson<T: JSONSerializable>(url: string): Promise<T> {
|
||||
const response = await fetchLocal(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
||||
const response = await requestLocal(url);
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(`HTTP ${response.statusCode}`);
|
||||
}
|
||||
return response.json();
|
||||
if (!response.headers.get('Content-Type')?.startsWith('application/json')) {
|
||||
throw new Error('Expected Content-Type: application/json');
|
||||
}
|
||||
return JSON.parse(response.bodyBuffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the global fetch dispatcher to allow self-signed certificates.
|
||||
* This runs with Jest's `beforeAll` and `afterAll`, and restores the original dispatcher.
|
||||
*/
|
||||
export function withFetchSelfSignedCertsForAllTests() {
|
||||
const fetchOriginal = globalThis.fetch;
|
||||
export function withFetchSelfSignedCertsForAllTests(
|
||||
fetchSpy: JestMockFn<Parameters<typeof fetch>, ReturnType<typeof fetch>>,
|
||||
fetchOriginal: typeof fetch,
|
||||
) {
|
||||
const selfSignedCertDispatcher = new Agent({
|
||||
connect: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
let fetchSpy;
|
||||
|
||||
beforeAll(() => {
|
||||
// For some reason, setting the `selfSignedCertDispatcher` with `setGlobalDispatcher` doesn't work.
|
||||
// Instead of using `setGlobalDispatcher`, we'll use a spy to intercept the fetch calls and add the dispatcher.
|
||||
fetchSpy = jest
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation((url, options) =>
|
||||
fetchOriginal(url, {
|
||||
...options,
|
||||
dispatcher: options?.dispatcher ?? selfSignedCertDispatcher,
|
||||
}),
|
||||
);
|
||||
fetchSpy.mockImplementation((url, options) =>
|
||||
fetchOriginal(url, {
|
||||
...options,
|
||||
// $FlowFixMe[prop-missing]: dispatcher
|
||||
dispatcher: options?.dispatcher ?? selfSignedCertDispatcher,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
||||
@@ -23,10 +23,11 @@ export class DebuggerAgent {
|
||||
#ws: ?WebSocket;
|
||||
#readyPromise: Promise<void>;
|
||||
|
||||
constructor(url: string, signal?: AbortSignal) {
|
||||
constructor(url: string, signal?: AbortSignal, hostHeader?: ?string) {
|
||||
const ws = new WebSocket(url, {
|
||||
// The mock server uses a self-signed certificate.
|
||||
rejectUnauthorized: false,
|
||||
...(hostHeader != null ? {headers: {Host: hostHeader}} : {}),
|
||||
});
|
||||
this.#ws = ws;
|
||||
ws.on('message', data => {
|
||||
@@ -115,8 +116,9 @@ export class DebuggerMock extends DebuggerAgent {
|
||||
export async function createDebuggerMock(
|
||||
url: string,
|
||||
signal: AbortSignal,
|
||||
hostHeader?: ?string,
|
||||
): Promise<DebuggerMock> {
|
||||
const debuggerMock = new DebuggerMock(url, signal);
|
||||
const debuggerMock = new DebuggerMock(url, signal, hostHeader);
|
||||
await debuggerMock.ready();
|
||||
return debuggerMock;
|
||||
}
|
||||
|
||||
@@ -27,10 +27,17 @@ export class DeviceAgent {
|
||||
#ws: ?WebSocket;
|
||||
#readyPromise: Promise<void>;
|
||||
|
||||
constructor(url: string, signal?: AbortSignal) {
|
||||
constructor(url: string, signal?: AbortSignal, host?: ?string) {
|
||||
const ws = new WebSocket(url, {
|
||||
// The mock server uses a self-signed certificate.
|
||||
rejectUnauthorized: false,
|
||||
...(host != null
|
||||
? {
|
||||
headers: {
|
||||
Host: host,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
this.#ws = ws;
|
||||
ws.on('message', data => {
|
||||
@@ -160,8 +167,9 @@ export class DeviceMock extends DeviceAgent {
|
||||
export async function createDeviceMock(
|
||||
url: string,
|
||||
signal: AbortSignal,
|
||||
host?: ?string,
|
||||
): Promise<DeviceMock> {
|
||||
const device = new DeviceMock(url, signal);
|
||||
const device = new DeviceMock(url, signal, host);
|
||||
await device.ready();
|
||||
return device;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,15 @@ export async function createAndConnectTarget(
|
||||
}>,
|
||||
signal: AbortSignal,
|
||||
page: PageFromDevice,
|
||||
deviceId: ?string = null,
|
||||
{
|
||||
debuggerHostHeader = null,
|
||||
deviceId = null,
|
||||
deviceHostHeader = null,
|
||||
}: $ReadOnly<{
|
||||
debuggerHostHeader?: ?string,
|
||||
deviceId?: ?string,
|
||||
deviceHostHeader?: ?string,
|
||||
}> = {},
|
||||
): Promise<{device: DeviceMock, debugger_: DebuggerMock}> {
|
||||
let device;
|
||||
let debugger_;
|
||||
@@ -136,6 +144,7 @@ export async function createAndConnectTarget(
|
||||
deviceId ?? 'device' + Date.now()
|
||||
}&name=foo&app=bar`,
|
||||
signal,
|
||||
deviceHostHeader,
|
||||
);
|
||||
device.getPages.mockImplementation(() => [page]);
|
||||
|
||||
@@ -150,7 +159,11 @@ export async function createAndConnectTarget(
|
||||
const [{webSocketDebuggerUrl}] = pageList;
|
||||
expect(webSocketDebuggerUrl).toBeDefined();
|
||||
|
||||
debugger_ = await createDebuggerMock(webSocketDebuggerUrl, signal);
|
||||
debugger_ = await createDebuggerMock(
|
||||
webSocketDebuggerUrl,
|
||||
signal,
|
||||
debuggerHostHeader,
|
||||
);
|
||||
await until(() => expect(device.connect).toBeCalled());
|
||||
} catch (e) {
|
||||
device?.close();
|
||||
|
||||
@@ -33,12 +33,18 @@ jest.useRealTimers();
|
||||
|
||||
jest.setTimeout(10000);
|
||||
|
||||
const fetchOriginal = fetch;
|
||||
const fetchSpy: JestMockFn<
|
||||
Parameters<typeof fetch>,
|
||||
ReturnType<typeof fetch>,
|
||||
> = jest.spyOn(globalThis, 'fetch');
|
||||
|
||||
describe.each(['HTTP', 'HTTPS'])(
|
||||
'inspector proxy CDP rewriting hacks over %s',
|
||||
protocol => {
|
||||
// Inspector proxy tests are using a self-signed certificate for HTTPS tests.
|
||||
if (protocol === 'HTTPS') {
|
||||
withFetchSelfSignedCertsForAllTests();
|
||||
withFetchSelfSignedCertsForAllTests(fetchSpy, fetchOriginal);
|
||||
}
|
||||
|
||||
const serverRef = withServerForEachTest({
|
||||
@@ -46,6 +52,7 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
projectRoot: __dirname,
|
||||
secure: protocol === 'HTTPS',
|
||||
});
|
||||
|
||||
const autoCleanup = withAbortSignalForEachTest();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -188,7 +195,52 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
}
|
||||
});
|
||||
|
||||
describe.each(['10.0.2.2', '10.0.3.2', '127.0.0.1'])(
|
||||
test("does not rewrite urls in Debugger.scriptParsed that don't match the device connection host", async () => {
|
||||
serverRef.app.use('/source-map', serveStaticJson({version: 3}));
|
||||
const {device, debugger_} = await createAndConnectTarget(
|
||||
serverRef,
|
||||
autoCleanup.signal,
|
||||
{
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
{
|
||||
deviceHostHeader: '192.168.0.123:' + serverRef.port,
|
||||
},
|
||||
);
|
||||
try {
|
||||
let fetchCalledWithURL;
|
||||
fetchSpy.mockImplementationOnce(async url => {
|
||||
fetchCalledWithURL = url instanceof URL ? url : null;
|
||||
throw new Error('Unreachable');
|
||||
});
|
||||
const sourceMapURL = `${protocol.toLowerCase()}://127.0.0.1:${
|
||||
serverRef.port
|
||||
}/source-map`;
|
||||
const scriptParsedMessage = await sendFromTargetToDebugger(
|
||||
device,
|
||||
debugger_,
|
||||
'page1',
|
||||
{
|
||||
method: 'Debugger.scriptParsed',
|
||||
params: {
|
||||
sourceMapURL,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(fetchCalledWithURL?.href).toEqual(sourceMapURL);
|
||||
expect(scriptParsedMessage.params.sourceMapURL).toEqual(
|
||||
`${protocol.toLowerCase()}://127.0.0.1:${serverRef.port}/source-map`,
|
||||
);
|
||||
} finally {
|
||||
device.close();
|
||||
debugger_.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe.each(['10.0.2.2:8080', '[::1]', 'example.com:2000'])(
|
||||
'%s aliasing to and from localhost',
|
||||
sourceHost => {
|
||||
test('in source map fetching during Debugger.scriptParsed', async () => {
|
||||
@@ -202,6 +254,9 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
{
|
||||
deviceHostHeader: sourceHost,
|
||||
},
|
||||
);
|
||||
try {
|
||||
const scriptParsedMessage = await sendFromTargetToDebugger(
|
||||
@@ -211,9 +266,7 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
{
|
||||
method: 'Debugger.scriptParsed',
|
||||
params: {
|
||||
sourceMapURL: `${protocol.toLowerCase()}://${sourceHost}:${
|
||||
serverRef.port
|
||||
}/source-map`,
|
||||
sourceMapURL: `${protocol.toLowerCase()}://${sourceHost}/source-map`,
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -236,6 +289,10 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
{
|
||||
debuggerHostHeader: 'localhost:' + serverRef.port,
|
||||
deviceHostHeader: sourceHost,
|
||||
},
|
||||
);
|
||||
try {
|
||||
const scriptParsedMessage = await sendFromTargetToDebugger(
|
||||
@@ -245,9 +302,7 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
{
|
||||
method: 'Debugger.scriptParsed',
|
||||
params: {
|
||||
url: `${protocol.toLowerCase()}://${sourceHost}:${
|
||||
serverRef.port
|
||||
}/some/file.js`,
|
||||
url: `${protocol.toLowerCase()}://${sourceHost}/some/file.js`,
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -273,9 +328,7 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
},
|
||||
);
|
||||
expect(setBreakpointByUrlMessage.params.url).toEqual(
|
||||
`${protocol.toLowerCase()}://${sourceHost}:${
|
||||
serverRef.port
|
||||
}/some/file.js`,
|
||||
`${protocol.toLowerCase()}://${sourceHost}/some/file.js`,
|
||||
);
|
||||
|
||||
const setBreakpointByUrlRegexMessage =
|
||||
@@ -284,12 +337,23 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
method: 'Debugger.setBreakpointByUrl',
|
||||
params: {
|
||||
lineNumber: 1,
|
||||
urlRegex: 'localhost:1000|localhost:2000',
|
||||
urlRegex: `localhost:${serverRef.port}|example.com:2000`,
|
||||
},
|
||||
});
|
||||
expect(setBreakpointByUrlRegexMessage.params.urlRegex).toEqual(
|
||||
`${sourceHost}:1000|${sourceHost}:2000`,
|
||||
);
|
||||
|
||||
// urlRegex rewriting is restricted to specific Android IPs that
|
||||
// are well-known to route to the host. In this case we only
|
||||
// replace hostname - longstanding behaviour.
|
||||
if (sourceHost === '10.0.2.2:8080') {
|
||||
expect(setBreakpointByUrlRegexMessage.params.urlRegex).toEqual(
|
||||
`10\\.0\\.2\\.2:${serverRef.port}|example.com:2000`,
|
||||
);
|
||||
} else {
|
||||
// Otherwise expect no change.
|
||||
expect(setBreakpointByUrlRegexMessage.params.urlRegex).toEqual(
|
||||
`localhost:${serverRef.port}|example.com:2000`,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
device.close();
|
||||
debugger_.close();
|
||||
@@ -307,6 +371,9 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
{
|
||||
deviceHostHeader: sourceHost,
|
||||
},
|
||||
);
|
||||
try {
|
||||
const response = await debugger_.sendAndGetResponse({
|
||||
@@ -344,6 +411,9 @@ describe.each(['HTTP', 'HTTPS'])(
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
{
|
||||
deviceHostHeader: '127.0.0.1:' + serverRef.port,
|
||||
},
|
||||
);
|
||||
try {
|
||||
const scriptParsedMessage = await sendFromTargetToDebugger(
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
} from '../inspector-proxy/types';
|
||||
|
||||
import DefaultBrowserLauncher from '../utils/DefaultBrowserLauncher';
|
||||
import {fetchJson, fetchLocal} from './FetchUtils';
|
||||
import {fetchJson, requestLocal} from './FetchUtils';
|
||||
import {createDeviceMock} from './InspectorDeviceUtils';
|
||||
import {withAbortSignalForEachTest} from './ResourceUtils';
|
||||
import {withServerForEachTest} from './ServerUtils';
|
||||
@@ -362,7 +362,7 @@ describe('inspector proxy HTTP API', () => {
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const response = await fetchLocal(
|
||||
const response = await requestLocal(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
expect(response.headers.get('Content-Length')).not.toBeNull();
|
||||
@@ -415,10 +415,12 @@ describe('inspector proxy HTTP API', () => {
|
||||
);
|
||||
openUrl.searchParams.set('target', firstPage.id);
|
||||
// Request to open the debugger for the first device
|
||||
const response = await fetchLocal(openUrl.toString(), {method: 'POST'});
|
||||
const response = await requestLocal(openUrl.toString(), {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
// Ensure the request was handled properly
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.statusCode).toBe(200);
|
||||
// Ensure the debugger was launched
|
||||
expect(launchDebuggerSpy).toHaveBeenCalledWith(expect.any(String));
|
||||
} finally {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import {fetchLocal} from './FetchUtils';
|
||||
import {requestLocal} from './FetchUtils';
|
||||
import {withServerForEachTest} from './ServerUtils';
|
||||
|
||||
jest.useRealTimers();
|
||||
@@ -22,11 +22,10 @@ describe('embedder script', () => {
|
||||
});
|
||||
|
||||
test('is always served', async () => {
|
||||
const resp = await fetchLocal(
|
||||
const resp = await requestLocal(
|
||||
serverRef.serverBaseUrl +
|
||||
'/debugger-frontend/embedder-static/embedderScript.js',
|
||||
);
|
||||
expect(resp.ok).toBeTruthy();
|
||||
expect(resp.status).toBe(200);
|
||||
expect(resp.statusCode).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import type {CreateCustomMessageHandlerFn} from './inspector-proxy/CustomMessageHandler';
|
||||
import type {BrowserLauncher} from './types/BrowserLauncher';
|
||||
import type {EventReporter} from './types/EventReporter';
|
||||
import type {EventReporter, ReportableEvent} from './types/EventReporter';
|
||||
import type {Experiments, ExperimentsConfig} from './types/Experiments';
|
||||
import type {Logger} from './types/Logger';
|
||||
import type {NextHandleFunction} from 'connect';
|
||||
@@ -81,11 +81,15 @@ export default function createDevMiddleware({
|
||||
unstable_customInspectorMessageHandler,
|
||||
}: Options): DevMiddlewareAPI {
|
||||
const experiments = getExperiments(experimentConfig);
|
||||
const eventReporter = createWrappedEventReporter(
|
||||
unstable_eventReporter,
|
||||
logger,
|
||||
);
|
||||
|
||||
const inspectorProxy = new InspectorProxy(
|
||||
projectRoot,
|
||||
serverBaseUrl,
|
||||
unstable_eventReporter,
|
||||
eventReporter,
|
||||
experiments,
|
||||
unstable_customInspectorMessageHandler,
|
||||
);
|
||||
@@ -97,7 +101,7 @@ export default function createDevMiddleware({
|
||||
serverBaseUrl,
|
||||
inspectorProxy,
|
||||
browserLauncher: unstable_browserLauncher,
|
||||
eventReporter: unstable_eventReporter,
|
||||
eventReporter,
|
||||
experiments,
|
||||
logger,
|
||||
}),
|
||||
@@ -129,3 +133,26 @@ function getExperiments(config: ExperimentsConfig): Experiments {
|
||||
enableNetworkInspector: config.enableNetworkInspector ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapped EventReporter that locally intercepts events to
|
||||
* log to the terminal.
|
||||
*/
|
||||
function createWrappedEventReporter(
|
||||
reporter: ?EventReporter,
|
||||
logger: ?Logger,
|
||||
): EventReporter {
|
||||
return {
|
||||
logEvent(event: ReportableEvent) {
|
||||
switch (event.type) {
|
||||
case 'profiling_target_registered':
|
||||
logger?.info(
|
||||
`Profiling build target "${event.appId}" registered for debugging`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
reporter?.logEvent(event);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,22 +37,6 @@ const debug = require('debug')('Metro:InspectorProxy');
|
||||
|
||||
const PAGES_POLLING_INTERVAL = 1000;
|
||||
|
||||
// Replace hosts appearing in the `url` and `sourceMapURL` fields of
|
||||
// `Debugger.scriptParsed`, and back again in messages from the debugger,
|
||||
// to account for device/debugger/proxy running on different networks.
|
||||
const REWRITE_HOSTS_TO_LOCALHOST: Array<string> = [
|
||||
// A device may retrieve a bundle through 127.0.0.1 via a (SSH) tunnel, but
|
||||
// the (remote) Metro server may be on a host without an IPv4 loopback, so
|
||||
// 127.0.0.1 may not be addressible locally for (e.g., for source map
|
||||
// fetching). Replacing with the more general 'localhost' should always be
|
||||
// safe while also more compatible with IPv6-only setups.
|
||||
'127.0.0.1',
|
||||
// Android's stock emulator and other emulators such as genymotion use a
|
||||
// standard localhost alias.
|
||||
'10.0.2.2',
|
||||
'10.0.3.2',
|
||||
];
|
||||
|
||||
// Prefix for script URLs that are alphanumeric IDs. See comment in #processMessageFromDeviceLegacy method for
|
||||
// more details.
|
||||
const FILE_PREFIX = 'file://';
|
||||
@@ -60,17 +44,28 @@ const FILE_PREFIX = 'file://';
|
||||
type DebuggerConnection = {
|
||||
// Debugger web socket connection
|
||||
socket: WS,
|
||||
// If we replaced address (like '10.0.2.2') to localhost we need to store original
|
||||
// address because Chrome uses URL or urlRegex params (instead of scriptId) to set breakpoints.
|
||||
originalSourceURLAddress?: string,
|
||||
prependedFilePrefix: boolean,
|
||||
pageId: string,
|
||||
userAgent: string | null,
|
||||
customHandler: ?CustomMessageHandler,
|
||||
debuggerRelativeBaseUrl: URL,
|
||||
};
|
||||
|
||||
const REACT_NATIVE_RELOADABLE_PAGE_ID = '-1';
|
||||
|
||||
export type DeviceOptions = $ReadOnly<{
|
||||
id: string,
|
||||
name: string,
|
||||
app: string,
|
||||
socket: WS,
|
||||
projectRoot: string,
|
||||
eventReporter: ?EventReporter,
|
||||
createMessageMiddleware: ?CreateCustomMessageHandlerFn,
|
||||
deviceRelativeBaseUrl: URL,
|
||||
serverRelativeBaseUrl: URL,
|
||||
isProfilingBuild: boolean,
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Device class represents single device connection to Inspector Proxy. Each device
|
||||
* can have multiple inspectable pages.
|
||||
@@ -125,41 +120,36 @@ export default class Device {
|
||||
|
||||
#connectedPageIds: Set<string> = new Set();
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
app: string,
|
||||
socket: WS,
|
||||
projectRoot: string,
|
||||
eventReporter: ?EventReporter,
|
||||
createMessageMiddleware: ?CreateCustomMessageHandlerFn,
|
||||
serverBaseUrl?: URL,
|
||||
) {
|
||||
this.#dangerouslyConstruct(
|
||||
id,
|
||||
name,
|
||||
app,
|
||||
socket,
|
||||
projectRoot,
|
||||
eventReporter,
|
||||
createMessageMiddleware,
|
||||
);
|
||||
// A base HTTP(S) URL to this server, reachable from the device. Derived from
|
||||
// the http request that created the connection.
|
||||
#deviceRelativeBaseUrl: URL;
|
||||
|
||||
// A base HTTP(S) URL to the server, relative to this server.
|
||||
#serverRelativeBaseUrl: URL;
|
||||
|
||||
constructor(deviceOptions: DeviceOptions) {
|
||||
this.#dangerouslyConstruct(deviceOptions);
|
||||
}
|
||||
|
||||
#dangerouslyConstruct(
|
||||
id: string,
|
||||
name: string,
|
||||
app: string,
|
||||
socket: WS,
|
||||
projectRoot: string,
|
||||
eventReporter: ?EventReporter,
|
||||
createMessageMiddleware: ?CreateCustomMessageHandlerFn,
|
||||
) {
|
||||
#dangerouslyConstruct({
|
||||
id,
|
||||
name,
|
||||
app,
|
||||
socket,
|
||||
projectRoot,
|
||||
eventReporter,
|
||||
createMessageMiddleware,
|
||||
serverRelativeBaseUrl,
|
||||
deviceRelativeBaseUrl,
|
||||
isProfilingBuild,
|
||||
}: DeviceOptions) {
|
||||
this.#id = id;
|
||||
this.#name = name;
|
||||
this.#app = app;
|
||||
this.#deviceSocket = socket;
|
||||
this.#projectRoot = projectRoot;
|
||||
this.#serverRelativeBaseUrl = serverRelativeBaseUrl;
|
||||
this.#deviceRelativeBaseUrl = deviceRelativeBaseUrl;
|
||||
this.#deviceEventReporter = eventReporter
|
||||
? new DeviceEventReporter(eventReporter, {
|
||||
deviceId: id,
|
||||
@@ -169,6 +159,10 @@ export default class Device {
|
||||
: null;
|
||||
this.#createCustomMessageHandler = createMessageMiddleware;
|
||||
|
||||
if (isProfilingBuild) {
|
||||
this.#deviceEventReporter?.logProfilingTargetRegistered();
|
||||
}
|
||||
|
||||
// $FlowFixMe[incompatible-call]
|
||||
this.#deviceSocket.on('message', (message: string) => {
|
||||
this.#messageFromDeviceQueue = this.#messageFromDeviceQueue
|
||||
@@ -239,23 +233,15 @@ export default class Device {
|
||||
* This hack attempts to allow users to reload the app, either as result of a
|
||||
* crash, or manually reloading, without having to restart the debugger.
|
||||
*/
|
||||
dangerouslyRecreateDevice(
|
||||
id: string,
|
||||
name: string,
|
||||
app: string,
|
||||
socket: WS,
|
||||
projectRoot: string,
|
||||
eventReporter: ?EventReporter,
|
||||
createMessageMiddleware: ?CreateCustomMessageHandlerFn,
|
||||
) {
|
||||
dangerouslyRecreateDevice(deviceOptions: DeviceOptions) {
|
||||
invariant(
|
||||
id === this.#id,
|
||||
deviceOptions.id === this.#id,
|
||||
'dangerouslyRecreateDevice() can only be used for the same device ID',
|
||||
);
|
||||
|
||||
const oldDebugger = this.#debuggerConnection;
|
||||
|
||||
if (this.#app !== app || this.#name !== name) {
|
||||
if (this.#app !== deviceOptions.app || this.#name !== deviceOptions.name) {
|
||||
this.#deviceSocket.close();
|
||||
this.#terminateDebuggerConnection();
|
||||
}
|
||||
@@ -266,19 +252,12 @@ export default class Device {
|
||||
oldDebugger.socket.removeAllListeners();
|
||||
this.#deviceSocket.close();
|
||||
this.handleDebuggerConnection(oldDebugger.socket, oldDebugger.pageId, {
|
||||
debuggerRelativeBaseUrl: oldDebugger.debuggerRelativeBaseUrl,
|
||||
userAgent: oldDebugger.userAgent,
|
||||
});
|
||||
}
|
||||
|
||||
this.#dangerouslyConstruct(
|
||||
id,
|
||||
name,
|
||||
app,
|
||||
socket,
|
||||
projectRoot,
|
||||
eventReporter,
|
||||
createMessageMiddleware,
|
||||
);
|
||||
this.#dangerouslyConstruct(deviceOptions);
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
@@ -304,7 +283,11 @@ export default class Device {
|
||||
handleDebuggerConnection(
|
||||
socket: WS,
|
||||
pageId: string,
|
||||
metadata: $ReadOnly<{
|
||||
{
|
||||
debuggerRelativeBaseUrl,
|
||||
userAgent,
|
||||
}: $ReadOnly<{
|
||||
debuggerRelativeBaseUrl: URL,
|
||||
userAgent: string | null,
|
||||
}>,
|
||||
) {
|
||||
@@ -315,7 +298,8 @@ export default class Device {
|
||||
|
||||
if (!page) {
|
||||
debug(
|
||||
`Got new debugger connection for page ${pageId} of ${this.#name}, but no such page exists`,
|
||||
`Got new debugger connection via ${debuggerRelativeBaseUrl.href} for ` +
|
||||
`page ${pageId} of ${this.#name}, but no such page exists`,
|
||||
);
|
||||
socket.close();
|
||||
return;
|
||||
@@ -329,20 +313,24 @@ export default class Device {
|
||||
|
||||
this.#deviceEventReporter?.logConnection('debugger', {
|
||||
pageId,
|
||||
frontendUserAgent: metadata.userAgent,
|
||||
frontendUserAgent: userAgent,
|
||||
});
|
||||
|
||||
const debuggerInfo = {
|
||||
socket,
|
||||
prependedFilePrefix: false,
|
||||
pageId,
|
||||
userAgent: metadata.userAgent,
|
||||
userAgent: userAgent,
|
||||
customHandler: null,
|
||||
debuggerRelativeBaseUrl,
|
||||
};
|
||||
|
||||
this.#debuggerConnection = debuggerInfo;
|
||||
|
||||
debug(`Got new debugger connection for page ${pageId} of ${this.#name}`);
|
||||
debug(
|
||||
`Got new debugger connection via ${debuggerRelativeBaseUrl.href} for ` +
|
||||
`page ${pageId} of ${this.#name}`,
|
||||
);
|
||||
|
||||
if (this.#debuggerConnection && this.#createCustomMessageHandler) {
|
||||
this.#debuggerConnection.customHandler = this.#createCustomMessageHandler(
|
||||
@@ -396,7 +384,7 @@ export default class Device {
|
||||
const debuggerRequest = JSON.parse(message);
|
||||
this.#deviceEventReporter?.logRequest(debuggerRequest, 'debugger', {
|
||||
pageId: this.#debuggerConnection?.pageId ?? null,
|
||||
frontendUserAgent: metadata.userAgent,
|
||||
frontendUserAgent: userAgent,
|
||||
prefersFuseboxFrontend: this.#isPageFuseboxFrontend(
|
||||
this.#debuggerConnection?.pageId,
|
||||
),
|
||||
@@ -673,6 +661,54 @@ export default class Device {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a URL from the debugger frontend, returns the equivalent URL
|
||||
* reachable from the device.
|
||||
*/
|
||||
#debuggerRelativeToDeviceRelativeUrl(
|
||||
debuggerRelativeUrl: URL,
|
||||
{debuggerRelativeBaseUrl}: DebuggerConnection,
|
||||
): URL {
|
||||
const deviceRelativeUrl = new URL(debuggerRelativeUrl.href);
|
||||
if (debuggerRelativeUrl.origin === debuggerRelativeBaseUrl.origin) {
|
||||
deviceRelativeUrl.hostname = this.#deviceRelativeBaseUrl.hostname;
|
||||
deviceRelativeUrl.port = this.#deviceRelativeBaseUrl.port;
|
||||
deviceRelativeUrl.protocol = this.#deviceRelativeBaseUrl.protocol;
|
||||
}
|
||||
return deviceRelativeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a URL from the device, returns the equivalent URL reachable from
|
||||
* the debugger frontend.
|
||||
*/
|
||||
#deviceRelativeUrlToDebuggerRelativeUrl(
|
||||
deviceRelativeUrl: URL,
|
||||
{debuggerRelativeBaseUrl}: DebuggerConnection,
|
||||
): URL {
|
||||
const debuggerRelativeUrl = new URL(deviceRelativeUrl.href);
|
||||
if (deviceRelativeUrl.origin === this.#deviceRelativeBaseUrl.origin) {
|
||||
debuggerRelativeUrl.hostname = debuggerRelativeBaseUrl.hostname;
|
||||
debuggerRelativeUrl.port = debuggerRelativeBaseUrl.port;
|
||||
debuggerRelativeUrl.protocol = debuggerRelativeUrl.protocol;
|
||||
}
|
||||
return debuggerRelativeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a URL from the device, returns the equivalent URL reachable from
|
||||
* this proxy.
|
||||
*/
|
||||
#deviceRelativeUrlToServerRelativeUrl(deviceRelativeUrl: URL): URL {
|
||||
const debuggerRelativeUrl = new URL(deviceRelativeUrl.href);
|
||||
if (deviceRelativeUrl.origin === this.#deviceRelativeBaseUrl.origin) {
|
||||
debuggerRelativeUrl.hostname = this.#serverRelativeBaseUrl.hostname;
|
||||
debuggerRelativeUrl.port = this.#serverRelativeBaseUrl.port;
|
||||
debuggerRelativeUrl.protocol = this.#serverRelativeBaseUrl.protocol;
|
||||
}
|
||||
return debuggerRelativeUrl;
|
||||
}
|
||||
|
||||
// Allows to make changes in incoming message from device.
|
||||
async #processMessageFromDeviceLegacy(
|
||||
payload: CDPServerMessage,
|
||||
@@ -692,25 +728,25 @@ export default class Device {
|
||||
) {
|
||||
const params = payload.params;
|
||||
if ('sourceMapURL' in params) {
|
||||
for (const hostToRewrite of REWRITE_HOSTS_TO_LOCALHOST) {
|
||||
if (params.sourceMapURL.includes(hostToRewrite)) {
|
||||
payload.params.sourceMapURL = params.sourceMapURL.replace(
|
||||
hostToRewrite,
|
||||
'localhost',
|
||||
);
|
||||
debuggerInfo.originalSourceURLAddress = hostToRewrite;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceMapURL = this.#tryParseHTTPURL(params.sourceMapURL);
|
||||
if (sourceMapURL) {
|
||||
// Rewrite device-relative URLs to de debugger-relative URLs for the
|
||||
// frontend.
|
||||
payload.params.sourceMapURL =
|
||||
this.#deviceRelativeUrlToDebuggerRelativeUrl(
|
||||
sourceMapURL,
|
||||
debuggerInfo,
|
||||
).href;
|
||||
|
||||
// Some debug clients do not support fetching HTTP URLs. If the
|
||||
// message headed to the debug client identifies the source map with
|
||||
// an HTTP URL, fetch the content here and convert the content to a
|
||||
// Data URL (which is more widely supported) before passing the
|
||||
// message to the debug client.
|
||||
try {
|
||||
const sourceMap = await this.#fetchText(sourceMapURL);
|
||||
const sourceMap = await this.#fetchText(
|
||||
this.#deviceRelativeUrlToServerRelativeUrl(sourceMapURL),
|
||||
);
|
||||
payload.params.sourceMapURL =
|
||||
'data:application/json;charset=utf-8;base64,' +
|
||||
Buffer.from(sourceMap).toString('base64');
|
||||
@@ -722,11 +758,19 @@ export default class Device {
|
||||
}
|
||||
}
|
||||
if ('url' in params) {
|
||||
for (const hostToRewrite of REWRITE_HOSTS_TO_LOCALHOST) {
|
||||
if (params.url.includes(hostToRewrite)) {
|
||||
payload.params.url = params.url.replace(hostToRewrite, 'localhost');
|
||||
debuggerInfo.originalSourceURLAddress = hostToRewrite;
|
||||
}
|
||||
let serverRelativeUrl = params.url;
|
||||
const parsedUrl = this.#tryParseHTTPURL(params.url);
|
||||
if (parsedUrl) {
|
||||
// Rewrite device-relative URLs pointing to the server so that they're
|
||||
// reachable from the frontend.
|
||||
payload.params.url = this.#deviceRelativeUrlToDebuggerRelativeUrl(
|
||||
parsedUrl,
|
||||
debuggerInfo,
|
||||
).href;
|
||||
|
||||
// Determine the server-relative URL.
|
||||
serverRelativeUrl =
|
||||
this.#deviceRelativeUrlToServerRelativeUrl(parsedUrl).href;
|
||||
}
|
||||
|
||||
// Chrome doesn't download source maps if URL param is not a valid
|
||||
@@ -738,9 +782,13 @@ export default class Device {
|
||||
debuggerInfo.prependedFilePrefix = true;
|
||||
}
|
||||
|
||||
// $FlowFixMe[prop-missing]
|
||||
if (params.scriptId != null) {
|
||||
this.#scriptIdToSourcePathMapping.set(params.scriptId, params.url);
|
||||
if ('scriptId' in params && params.scriptId != null) {
|
||||
// Set a server-relative URL to locally fetch source by script ID
|
||||
// on Debugger.getScriptSource.
|
||||
this.#scriptIdToSourcePathMapping.set(
|
||||
params.scriptId,
|
||||
serverRelativeUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -835,36 +883,52 @@ export default class Device {
|
||||
debuggerInfo: DebuggerConnection,
|
||||
): CDPRequest<'Debugger.setBreakpointByUrl'> {
|
||||
// If we replaced Android emulator's address to localhost we need to change it back.
|
||||
if (debuggerInfo.originalSourceURLAddress != null) {
|
||||
const processedReq = {...req, params: {...req.params}};
|
||||
if (processedReq.params.url != null) {
|
||||
processedReq.params.url = processedReq.params.url.replace(
|
||||
'localhost',
|
||||
debuggerInfo.originalSourceURLAddress,
|
||||
);
|
||||
const {debuggerRelativeBaseUrl, prependedFilePrefix} = debuggerInfo;
|
||||
|
||||
if (
|
||||
processedReq.params.url &&
|
||||
processedReq.params.url.startsWith(FILE_PREFIX) &&
|
||||
debuggerInfo.prependedFilePrefix
|
||||
) {
|
||||
// Remove fake URL prefix if we modified URL in #processMessageFromDeviceLegacy.
|
||||
// $FlowFixMe[incompatible-use]
|
||||
processedReq.params.url = processedReq.params.url.slice(
|
||||
FILE_PREFIX.length,
|
||||
);
|
||||
}
|
||||
const processedReq = {...req, params: {...req.params}};
|
||||
if (processedReq.params.url != null) {
|
||||
const originalUrlParam = processedReq.params.url;
|
||||
const httpUrl = this.#tryParseHTTPURL(originalUrlParam);
|
||||
if (httpUrl) {
|
||||
processedReq.params.url = this.#debuggerRelativeToDeviceRelativeUrl(
|
||||
httpUrl,
|
||||
debuggerInfo,
|
||||
).href;
|
||||
} else if (
|
||||
originalUrlParam.startsWith(FILE_PREFIX) &&
|
||||
prependedFilePrefix
|
||||
) {
|
||||
// Remove fake URL prefix if we modified URL in #processMessageFromDeviceLegacy.
|
||||
processedReq.params.url = originalUrlParam.slice(FILE_PREFIX.length);
|
||||
}
|
||||
if (processedReq.params.urlRegex != null) {
|
||||
processedReq.params.urlRegex = processedReq.params.urlRegex.replace(
|
||||
/localhost/g,
|
||||
// $FlowFixMe[incompatible-call]
|
||||
debuggerInfo.originalSourceURLAddress,
|
||||
);
|
||||
}
|
||||
return processedReq;
|
||||
}
|
||||
return req;
|
||||
|
||||
// Retain special case rewriting of localhost to device-relative IPs
|
||||
// within regex patterns. We don't rewrite the protocol here because
|
||||
// these patterns typically come from CDT reinterpreting the source URL
|
||||
// `file://host/path` into the regex `host/path|file://host/path`. See:
|
||||
//
|
||||
// https://github.com/ChromeDevTools/devtools-frontend/blob/f913cc6d76f2e2639c05b11ba673fc880b5490dd/front_end/core/sdk/DebuggerModel.ts#L505
|
||||
//
|
||||
// This has always been fragile and probably unnecessary - we don't set
|
||||
// `file://` source URLs. It can be removed when we drop support for
|
||||
// legacy targets, if not sooner.
|
||||
if (
|
||||
// Android's stock emulator and other emulators such as genymotion use a
|
||||
// standard localhost alias.
|
||||
new Set(['10.0.2.2', '10.0.3.2']).has(
|
||||
this.#deviceRelativeBaseUrl.hostname,
|
||||
) &&
|
||||
debuggerRelativeBaseUrl.hostname === 'localhost' &&
|
||||
processedReq.params.urlRegex != null
|
||||
) {
|
||||
processedReq.params.urlRegex = processedReq.params.urlRegex.replaceAll(
|
||||
'localhost',
|
||||
// regex-escape IPv4
|
||||
this.#deviceRelativeBaseUrl.hostname.replaceAll('.', '\\.'),
|
||||
);
|
||||
}
|
||||
return processedReq;
|
||||
}
|
||||
|
||||
#processDebuggerGetScriptSource(
|
||||
@@ -907,6 +971,7 @@ export default class Device {
|
||||
if (pathToSource != null) {
|
||||
const httpURL = this.#tryParseHTTPURL(pathToSource);
|
||||
if (httpURL) {
|
||||
// URL is server-relatve, so we should be able to fetch it from here.
|
||||
this.#fetchText(httpURL).then(
|
||||
text => sendSuccessResponse(text),
|
||||
err =>
|
||||
|
||||
@@ -145,6 +145,17 @@ class DeviceEventReporter {
|
||||
});
|
||||
}
|
||||
|
||||
logProfilingTargetRegistered() {
|
||||
this.#eventReporter.logEvent({
|
||||
type: 'profiling_target_registered',
|
||||
status: 'success',
|
||||
appId: this.#metadata.appId,
|
||||
deviceName: this.#metadata.deviceName,
|
||||
deviceId: this.#metadata.deviceId,
|
||||
pageId: null,
|
||||
});
|
||||
}
|
||||
|
||||
logConnection(
|
||||
connectedEntity: 'debugger',
|
||||
metadata: $ReadOnly<{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import type {EventReporter} from '../types/EventReporter';
|
||||
import type {Experiments} from '../types/Experiments';
|
||||
import type {CreateCustomMessageHandlerFn} from './CustomMessageHandler';
|
||||
import type {DeviceOptions} from './Device';
|
||||
import type {
|
||||
JsonPagesListResponse,
|
||||
JsonVersionResponse,
|
||||
@@ -224,36 +225,38 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
const deviceId = query.device || fallbackDeviceId;
|
||||
const deviceName = query.name || 'Unknown';
|
||||
const appName = query.app || 'Unknown';
|
||||
const isProfilingBuild = query.profiling === 'true';
|
||||
|
||||
const deviceRelativeBaseUrl =
|
||||
getBaseUrlFromRequest(req) ?? this.#serverBaseUrl;
|
||||
|
||||
const oldDevice = this.#devices.get(deviceId);
|
||||
|
||||
let newDevice;
|
||||
const deviceOptions: DeviceOptions = {
|
||||
id: deviceId,
|
||||
name: deviceName,
|
||||
app: appName,
|
||||
socket,
|
||||
projectRoot: this.#projectRoot,
|
||||
eventReporter: this.#eventReporter,
|
||||
createMessageMiddleware: this.#customMessageHandler,
|
||||
deviceRelativeBaseUrl,
|
||||
serverRelativeBaseUrl: this.#serverBaseUrl,
|
||||
isProfilingBuild,
|
||||
};
|
||||
|
||||
if (oldDevice) {
|
||||
oldDevice.dangerouslyRecreateDevice(
|
||||
deviceId,
|
||||
deviceName,
|
||||
appName,
|
||||
socket,
|
||||
this.#projectRoot,
|
||||
this.#eventReporter,
|
||||
this.#customMessageHandler,
|
||||
);
|
||||
oldDevice.dangerouslyRecreateDevice(deviceOptions);
|
||||
newDevice = oldDevice;
|
||||
} else {
|
||||
newDevice = new Device(
|
||||
deviceId,
|
||||
deviceName,
|
||||
appName,
|
||||
socket,
|
||||
this.#projectRoot,
|
||||
this.#eventReporter,
|
||||
this.#customMessageHandler,
|
||||
);
|
||||
newDevice = new Device(deviceOptions);
|
||||
}
|
||||
|
||||
this.#devices.set(deviceId, newDevice);
|
||||
|
||||
debug(
|
||||
`Got new connection: name=${deviceName}, app=${appName}, device=${deviceId}`,
|
||||
`Got new connection: name=${deviceName}, app=${appName}, device=${deviceId}, via=${deviceRelativeBaseUrl.origin}`,
|
||||
);
|
||||
|
||||
socket.on('close', () => {
|
||||
@@ -289,6 +292,8 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
const query = url.parse(req.url || '', true).query || {};
|
||||
const deviceId = query.device;
|
||||
const pageId = query.page;
|
||||
const debuggerRelativeBaseUrl =
|
||||
getBaseUrlFromRequest(req) ?? this.#serverBaseUrl;
|
||||
|
||||
if (deviceId == null || pageId == null) {
|
||||
throw new Error('Incorrect URL - must provide device and page IDs');
|
||||
@@ -302,6 +307,7 @@ export default class InspectorProxy implements InspectorProxyQueries {
|
||||
this.#startHeartbeat(socket, DEBUGGER_HEARTBEAT_INTERVAL_MS);
|
||||
|
||||
device.handleDebuggerConnection(socket, pageId, {
|
||||
debuggerRelativeBaseUrl,
|
||||
userAgent: req.headers['user-agent'] ?? query.userAgent ?? null,
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -77,6 +77,11 @@ export type ReportableEvent =
|
||||
| 'PROTOCOL_ERROR',
|
||||
>,
|
||||
}
|
||||
| {
|
||||
type: 'profiling_target_registered',
|
||||
status: 'success',
|
||||
...DebuggerSessionIDs,
|
||||
}
|
||||
| {
|
||||
type: 'proxy_error',
|
||||
status: 'error',
|
||||
|
||||
@@ -24,5 +24,9 @@ export default function getBaseUrlFromRequest(
|
||||
// https://github.com/nodejs/node/issues/41863#issuecomment-1030709186
|
||||
const scheme = req.socket.encrypted === true ? 'https' : 'http';
|
||||
const url = `${scheme}://${req.headers.host}`;
|
||||
return URL.canParse(url) ? new URL(url) : null;
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
"bugs": "https://github.com/facebook/react-native/issues",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"babel-plugin-syntax-hermes-parser": "0.24.0",
|
||||
"hermes-eslint": "0.24.0"
|
||||
"babel-plugin-syntax-hermes-parser": "0.25.1",
|
||||
"hermes-eslint": "0.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"source-map-support": "0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-syntax-hermes-parser": "0.24.0",
|
||||
"hermes-eslint": "0.24.0"
|
||||
"babel-plugin-syntax-hermes-parser": "0.25.1",
|
||||
"hermes-eslint": "0.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ abstract class BuildCodegenCLITask : Exec() {
|
||||
}
|
||||
|
||||
override fun exec() {
|
||||
val logfile = "${project.layout.buildDirectory}/build-cli.log"
|
||||
val logfile = "${project.layout.buildDirectory.getAsFile().get()}/build-cli.log"
|
||||
File(logfile).apply {
|
||||
parentFile.mkdirs()
|
||||
if (exists()) {
|
||||
|
||||
@@ -10,7 +10,7 @@ buildscript {
|
||||
buildToolsVersion = "35.0.0"
|
||||
minSdkVersion = 24
|
||||
compileSdkVersion = 35
|
||||
targetSdkVersion = 34
|
||||
targetSdkVersion = 35
|
||||
ndkVersion = "27.1.12297006"
|
||||
kotlinVersion = "2.0.21"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <ReactCodegen/RCTAppDependencyProvider.h>
|
||||
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
||||
@@ -89,7 +89,6 @@ export function getDefaultConfig(projectRoot: string): ConfigT {
|
||||
babelTransformerPath: require.resolve(
|
||||
'@react-native/metro-babel-transformer',
|
||||
),
|
||||
hermesParser: true,
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"@babel/plugin-transform-unicode-regex": "^7.24.7",
|
||||
"@babel/template": "^7.25.0",
|
||||
"@react-native/babel-plugin-codegen": "0.77.0-main",
|
||||
"babel-plugin-syntax-hermes-parser": "0.24.0",
|
||||
"babel-plugin-syntax-hermes-parser": "0.25.1",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"react-refresh": "^0.14.0"
|
||||
},
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ function isTSXSource(fileName) {
|
||||
const loose = true;
|
||||
|
||||
const defaultPlugins = [
|
||||
[require('babel-plugin-syntax-hermes-parser')],
|
||||
[require('babel-plugin-syntax-hermes-parser'), {parseLangTypes: 'flow'}],
|
||||
[require('babel-plugin-transform-flow-enums')],
|
||||
[require('@babel/plugin-transform-block-scoping')],
|
||||
[require('@babel/plugin-transform-class-properties'), {loose}],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@react-native/babel-preset": "0.77.0-main",
|
||||
"hermes-parser": "0.24.0",
|
||||
"hermes-parser": "0.25.1",
|
||||
"nullthrows": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ type OnChangeEvent = $ReadOnly<{|
|
||||
source: {url: string, ...},
|
||||
x: Int32,
|
||||
y: Int32,
|
||||
arrayOfObjects: $ReadOnlyArray<{value: $ReadOnly<{str: string}>}>,
|
||||
...
|
||||
},
|
||||
|}>;
|
||||
|
||||
+14
@@ -161,6 +161,20 @@ void EventNestedObjectPropsNativeComponentViewEventEmitter::onChange(OnChange $e
|
||||
}
|
||||
location.setProperty(runtime, \\"x\\", $event.location.x);
|
||||
location.setProperty(runtime, \\"y\\", $event.location.y);
|
||||
|
||||
auto arrayOfObjects = jsi::Array(runtime, $event.location.arrayOfObjects.size());
|
||||
size_t arrayOfObjectsIndex = 0;
|
||||
for (auto arrayOfObjectsValue : $event.location.arrayOfObjects) {
|
||||
auto arrayOfObjectsObject = jsi::Object(runtime);
|
||||
{
|
||||
auto value = jsi::Object(runtime);
|
||||
value.setProperty(runtime, \\"str\\", arrayOfObjectsValue.value.str);
|
||||
arrayOfObjectsObject.setProperty(runtime, \\"value\\", value);
|
||||
}
|
||||
arrayOfObjects.setValueAtIndex(runtime, arrayOfObjectsIndex++, arrayOfObjectsObject);
|
||||
}
|
||||
location.setProperty(runtime, \\"arrayOfObjects\\", arrayOfObjects);
|
||||
|
||||
$payload.setProperty(runtime, \\"location\\", location);
|
||||
}
|
||||
return $payload;
|
||||
|
||||
+9
@@ -199,10 +199,19 @@ class EventNestedObjectPropsNativeComponentViewEventEmitter : public ViewEventEm
|
||||
std::string url;
|
||||
};
|
||||
|
||||
struct OnChangeLocationArrayOfObjectsValue {
|
||||
std::string str;
|
||||
};
|
||||
|
||||
struct OnChangeLocationArrayOfObjects {
|
||||
OnChangeLocationArrayOfObjectsValue value;
|
||||
};
|
||||
|
||||
struct OnChangeLocation {
|
||||
OnChangeLocationSource source;
|
||||
int x;
|
||||
int y;
|
||||
std::vector<OnChangeLocationArrayOfObjects> arrayOfObjects;
|
||||
};
|
||||
|
||||
struct OnChange {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.25.3",
|
||||
"glob": "^7.1.1",
|
||||
"hermes-parser": "0.24.0",
|
||||
"hermes-parser": "0.25.1",
|
||||
"invariant": "^2.2.4",
|
||||
"jscodeshift": "^17.0.0",
|
||||
"nullthrows": "^1.1.1",
|
||||
@@ -49,7 +49,7 @@
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"chalk": "^4.0.0",
|
||||
"hermes-estree": "0.24.0",
|
||||
"hermes-estree": "0.25.1",
|
||||
"micromatch": "^4.0.4",
|
||||
"prettier": "2.8.8",
|
||||
"rimraf": "^3.0.2"
|
||||
|
||||
+2
-2
@@ -104,7 +104,7 @@ function generateSetter(
|
||||
) {
|
||||
const eventChain = usingEvent
|
||||
? `$event.${[...propertyParts, propertyName].join('.')}`
|
||||
: [propertyParts, propertyName].join('.');
|
||||
: [...propertyParts, propertyName].join('.');
|
||||
return `${variableName}.setProperty(runtime, "${propertyName}", ${valueMapper(
|
||||
eventChain,
|
||||
)});`;
|
||||
@@ -157,7 +157,7 @@ function generateArraySetter(
|
||||
): string {
|
||||
const eventChain = usingEvent
|
||||
? `$event.${[...propertyParts, propertyName].join('.')}`
|
||||
: [propertyParts, propertyName].join('.');
|
||||
: [...propertyParts, propertyName].join('.');
|
||||
const indexVar = `${propertyName}Index`;
|
||||
const innerLoopVar = `${propertyName}Value`;
|
||||
return `
|
||||
|
||||
+1
-1
@@ -2673,7 +2673,7 @@ const UNION_MODULE: SchemaType = {
|
||||
},
|
||||
};
|
||||
|
||||
const STRING_LITERALS = {
|
||||
const STRING_LITERALS: SchemaType = {
|
||||
modules: {
|
||||
NativeSampleTurboModule: {
|
||||
type: 'NativeModule',
|
||||
|
||||
+3
-3
@@ -24,15 +24,15 @@ public class PopupMenuPackage() : BaseReactPackage(), ViewManagerOnDemandReactPa
|
||||
ModuleSpec.viewManagerSpec({ ReactPopupMenuManager() }),
|
||||
)
|
||||
|
||||
override fun getModule(name: String, context: ReactApplicationContext): NativeModule? {
|
||||
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
||||
return null
|
||||
}
|
||||
|
||||
protected override fun getViewManagers(context: ReactApplicationContext): List<ModuleSpec> {
|
||||
protected override fun getViewManagers(reactContext: ReactApplicationContext): List<ModuleSpec> {
|
||||
return viewManagersMap.values.toList()
|
||||
}
|
||||
|
||||
override fun getViewManagerNames(context: ReactApplicationContext): Collection<String> {
|
||||
override fun getViewManagerNames(reactContext: ReactApplicationContext): Collection<String> {
|
||||
return viewManagersMap.keys
|
||||
}
|
||||
|
||||
|
||||
@@ -73,8 +73,6 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
|
||||
|
||||
const useNativePropsInFabric =
|
||||
ReactNativeFeatureFlags.shouldUseSetNativePropsInFabric();
|
||||
const useSetNativePropsInNativeAnimationsInFabric =
|
||||
ReactNativeFeatureFlags.shouldUseSetNativePropsInNativeAnimationsInFabric();
|
||||
|
||||
const useAnimatedPropsLifecycle =
|
||||
ReactNativeFeatureFlags.useInsertionEffectsForAnimations()
|
||||
@@ -119,12 +117,7 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
|
||||
if (isFabricNode) {
|
||||
// Call `scheduleUpdate` to synchronise Fiber and Shadow tree.
|
||||
// Must not be called in Paper.
|
||||
if (useSetNativePropsInNativeAnimationsInFabric) {
|
||||
// $FlowFixMe[incompatible-use]
|
||||
instance.setNativeProps(node.__getAnimatedValue());
|
||||
} else {
|
||||
scheduleUpdate();
|
||||
}
|
||||
scheduleUpdate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -201,12 +194,7 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
|
||||
}
|
||||
};
|
||||
},
|
||||
[
|
||||
node,
|
||||
useNativePropsInFabric,
|
||||
useSetNativePropsInNativeAnimationsInFabric,
|
||||
props,
|
||||
],
|
||||
[node, useNativePropsInFabric, props],
|
||||
);
|
||||
const callbackRef = useRefEffect<TInstance>(refEffect);
|
||||
|
||||
|
||||
@@ -314,6 +314,10 @@ class RCTAppDelegateBridgelessFeatureFlags : public ReactNativeFeatureFlagsDefau
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool enableFixForViewCommandRace() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
- (void)_setUpFeatureFlags
|
||||
|
||||
@@ -116,6 +116,8 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
_onLayout = async (event: ViewLayoutEvent) => {
|
||||
event.persist();
|
||||
|
||||
const oldFrame = this._frame;
|
||||
this._frame = event.nativeEvent.layout;
|
||||
if (!this._initialFrameHeight) {
|
||||
|
||||
@@ -567,9 +567,6 @@ export type ViewProps = $ReadOnly<{|
|
||||
* optimization. Set this property to `false` to disable this optimization and
|
||||
* ensure that this `View` exists in the native view hierarchy.
|
||||
*
|
||||
* @platform android
|
||||
* In Fabric, this prop is used in ios as well.
|
||||
*
|
||||
* See https://reactnative.dev/docs/view#collapsable
|
||||
*/
|
||||
collapsable?: ?boolean,
|
||||
|
||||
@@ -121,6 +121,12 @@ function reportException(
|
||||
const NativeExceptionsManager =
|
||||
require('./NativeExceptionsManager').default;
|
||||
if (NativeExceptionsManager) {
|
||||
if (isFatal) {
|
||||
if (global.RN$hasHandledFatalException?.()) {
|
||||
return;
|
||||
}
|
||||
global.RN$notifyOfFatalException?.();
|
||||
}
|
||||
NativeExceptionsManager.reportException(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
};
|
||||
const defaultSource = resolveAssetSource(props.defaultSource);
|
||||
const loadingIndicatorSource = resolveAssetSource(
|
||||
props.loadingIndicatorSource,
|
||||
);
|
||||
@@ -179,7 +178,6 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
|
||||
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
|
||||
* when making Flow check .android.js files. */
|
||||
headers: (source?.[0]?.headers || source?.headers: ?{[string]: string}),
|
||||
defaultSrc: defaultSource ? defaultSource.uri : null,
|
||||
loadingIndicatorSrc: loadingIndicatorSource
|
||||
? loadingIndicatorSource.uri
|
||||
: null,
|
||||
|
||||
@@ -82,6 +82,9 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
|
||||
},
|
||||
validAttributes: {
|
||||
blurRadius: true,
|
||||
defaultSource: {
|
||||
process: require('./resolveAssetSource'),
|
||||
},
|
||||
internal_analyticTag: true,
|
||||
resizeMethod: true,
|
||||
resizeMode: true,
|
||||
@@ -100,7 +103,6 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
|
||||
borderRadius: true,
|
||||
headers: true,
|
||||
shouldNotifyLoadEvents: true,
|
||||
defaultSrc: true,
|
||||
overlayColor: {
|
||||
process: require('../StyleSheet/processColor').default,
|
||||
},
|
||||
|
||||
@@ -121,7 +121,7 @@ const Presets = {
|
||||
'opacity',
|
||||
): LayoutAnimationConfig),
|
||||
linear: (create(500, 'linear', 'opacity'): LayoutAnimationConfig),
|
||||
spring: {
|
||||
spring: ({
|
||||
duration: 700,
|
||||
create: {
|
||||
type: 'linear',
|
||||
@@ -135,7 +135,7 @@ const Presets = {
|
||||
type: 'linear',
|
||||
property: 'opacity',
|
||||
},
|
||||
},
|
||||
}: LayoutAnimationConfig),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,8 +40,8 @@ const HermesBadge = (): Node => {
|
||||
const styles = StyleSheet.create({
|
||||
badge: {
|
||||
position: 'absolute',
|
||||
top: 8,
|
||||
right: 12,
|
||||
bottom: 8,
|
||||
},
|
||||
badgeText: {
|
||||
fontSize: 14,
|
||||
|
||||
+94
-49
@@ -28,8 +28,7 @@ describe('processBackgroundImage', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: 'linearGradient',
|
||||
start: {x: 0, y: 0.5},
|
||||
end: {x: 1, y: 0.5},
|
||||
direction: {type: 'angle', value: 90},
|
||||
colorStops: [
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -44,8 +43,7 @@ describe('processBackgroundImage', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: 'linearGradient',
|
||||
start: {x: 0, y: 0},
|
||||
end: {x: 1, y: 1},
|
||||
direction: {type: 'keyword', value: 'to bottom right'},
|
||||
colorStops: [
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -68,14 +66,13 @@ describe('processBackgroundImage', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should process a linear gradient with whitespaces in direction', () => {
|
||||
it('should process a linear gradient with whitespaces in direction keyword', () => {
|
||||
const input = 'linear-gradient(to bottom right, red, blue)';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: 'linearGradient',
|
||||
start: {x: 0, y: 0},
|
||||
end: {x: 1, y: 1},
|
||||
direction: {type: 'keyword', value: 'to bottom right'},
|
||||
colorStops: [
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -91,8 +88,7 @@ describe('processBackgroundImage', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: 'linearGradient',
|
||||
start: {x: 0, y: 0},
|
||||
end: {x: 1, y: 1},
|
||||
direction: {type: 'keyword', value: 'to bottom right'},
|
||||
colorStops: [
|
||||
{color: processColor('red'), position: 0.3},
|
||||
{color: processColor('blue'), position: 0.8},
|
||||
@@ -105,10 +101,7 @@ describe('processBackgroundImage', () => {
|
||||
const input = 'linear-gradient(45deg, red, blue)';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].type).toBe('linearGradient');
|
||||
expect(result[0].start.x).toBeCloseTo(0.146447, 5);
|
||||
expect(result[0].start.y).toBeCloseTo(0.853553, 5);
|
||||
expect(result[0].end.x).toBeCloseTo(0.853553, 5);
|
||||
expect(result[0].end.y).toBeCloseTo(0.146447, 5);
|
||||
expect(result[0].direction).toEqual({type: 'angle', value: 45});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -119,10 +112,41 @@ describe('processBackgroundImage', () => {
|
||||
const input = 'linear-gradient(45Deg, red, blue)';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].type).toBe('linearGradient');
|
||||
expect(result[0].start.x).toBeCloseTo(0.146447, 5);
|
||||
expect(result[0].start.y).toBeCloseTo(0.853553, 5);
|
||||
expect(result[0].end.x).toBeCloseTo(0.853553, 5);
|
||||
expect(result[0].end.y).toBeCloseTo(0.146447, 5);
|
||||
expect(result[0].direction).toEqual({type: 'angle', value: 45});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should process a linear gradient object style with case-insensitive direction keyword', () => {
|
||||
const input = [
|
||||
{
|
||||
type: 'linearGradient',
|
||||
direction: 'To Bottom',
|
||||
colorStops: [{color: 'red'}, {color: 'blue'}],
|
||||
},
|
||||
];
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].type).toBe('linearGradient');
|
||||
expect(result[0].direction).toEqual({type: 'angle', value: 180});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should process a linear gradient object style with case-insensitive angle', () => {
|
||||
const input = [
|
||||
{
|
||||
type: 'linearGradient',
|
||||
direction: '45DEG',
|
||||
colorStops: [{color: 'red'}, {color: 'blue'}],
|
||||
},
|
||||
];
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].type).toBe('linearGradient');
|
||||
expect(result[0].direction).toEqual({type: 'angle', value: 45});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -133,8 +157,10 @@ describe('processBackgroundImage', () => {
|
||||
const input = 'LiNeAr-GradieNt(To Bottom, Red, Blue)';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].type).toBe('linearGradient');
|
||||
expect(result[0].start).toEqual({x: 0.5, y: 0});
|
||||
expect(result[0].end).toEqual({x: 0.5, y: 1});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 180,
|
||||
});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -144,8 +170,10 @@ describe('processBackgroundImage', () => {
|
||||
it('should process a linear gradient with case-insensitive direction enum', () => {
|
||||
const input = 'linear-gradient(tO Right, red, blue)';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].start).toEqual({x: 0, y: 0.5});
|
||||
expect(result[0].end).toEqual({x: 1, y: 0.5});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 90,
|
||||
});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -154,10 +182,12 @@ describe('processBackgroundImage', () => {
|
||||
|
||||
it('should process a linear gradient with case-insensitive colors', () => {
|
||||
const input =
|
||||
'linear-gradient(to right, Rgba(0, 0, 0, 0.5), Blue, Hsla(0, 100%, 50%, 0.5))';
|
||||
'linear-gradient(TO LEFT, Rgba(0, 0, 0, 0.5), Blue, Hsla(0, 100%, 50%, 0.5))';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].start).toEqual({x: 0, y: 0.5});
|
||||
expect(result[0].end).toEqual({x: 1, y: 0.5});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 270,
|
||||
});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('rgba(0, 0, 0, 0.5)'), position: 0},
|
||||
{color: processColor('blue'), position: 0.5},
|
||||
@@ -167,20 +197,24 @@ describe('processBackgroundImage', () => {
|
||||
|
||||
it('should process multiple linear gradients', () => {
|
||||
const input = `
|
||||
linear-gradient(to right, red, blue),
|
||||
linear-gradient(to top, red, blue),
|
||||
linear-gradient(to bottom, green, yellow)`;
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].type).toEqual('linearGradient');
|
||||
expect(result[0].start).toEqual({x: 0, y: 0.5});
|
||||
expect(result[0].end).toEqual({x: 1, y: 0.5});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 0,
|
||||
});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
]);
|
||||
expect(result[1].type).toEqual('linearGradient');
|
||||
expect(result[1].start).toEqual({x: 0.5, y: 0});
|
||||
expect(result[1].end).toEqual({x: 0.5, y: 1});
|
||||
expect(result[1].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 180,
|
||||
});
|
||||
|
||||
expect(result[1].colorStops).toEqual([
|
||||
{color: processColor('green'), position: 0},
|
||||
@@ -190,20 +224,24 @@ describe('processBackgroundImage', () => {
|
||||
|
||||
it('should process multiple linear gradients with newlines', () => {
|
||||
const input = `
|
||||
linear-gradient(to right, red, blue),\n
|
||||
linear-gradient(to left, red, blue),\n
|
||||
linear-gradient(to bottom, green, yellow)`;
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].type).toEqual('linearGradient');
|
||||
expect(result[0].start).toEqual({x: 0, y: 0.5});
|
||||
expect(result[0].end).toEqual({x: 1, y: 0.5});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 270,
|
||||
});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
]);
|
||||
expect(result[1].type).toEqual('linearGradient');
|
||||
expect(result[1].start).toEqual({x: 0.5, y: 0});
|
||||
expect(result[1].end).toEqual({x: 0.5, y: 1});
|
||||
expect(result[1].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 180,
|
||||
});
|
||||
|
||||
expect(result[1].colorStops).toEqual([
|
||||
{color: processColor('green'), position: 0},
|
||||
@@ -212,7 +250,7 @@ describe('processBackgroundImage', () => {
|
||||
});
|
||||
|
||||
it('should process a linear gradient with multiple color stops', () => {
|
||||
const input = 'linear-gradient(to right, red 0%, green 50%, blue 100%)';
|
||||
const input = 'linear-gradient(to bottom, red 0%, green 50%, blue 100%)';
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('red'), position: 0},
|
||||
@@ -268,10 +306,14 @@ describe('processBackgroundImage', () => {
|
||||
linear-gradient(to bottom , rgba(255,0,0,0.9) , rgba(0,0,255,0.2) )`;
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].start).toEqual({x: 0, y: 0.5});
|
||||
expect(result[0].end).toEqual({x: 1, y: 0.5});
|
||||
expect(result[1].start).toEqual({x: 0.5, y: 0});
|
||||
expect(result[1].end).toEqual({x: 0.5, y: 1});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 90,
|
||||
});
|
||||
expect(result[1].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 180,
|
||||
});
|
||||
expect(result[0].colorStops).toEqual([
|
||||
{color: processColor('rgba(255,0,0,0.5)'), position: 0},
|
||||
{color: processColor('rgba(0,0,255,0.8)'), position: 1},
|
||||
@@ -321,8 +363,7 @@ describe('processBackgroundImage', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: 'linearGradient',
|
||||
start: {x: 0, y: 0},
|
||||
end: {x: 1, y: 1},
|
||||
direction: {type: 'keyword', value: 'to bottom right'},
|
||||
colorStops: [
|
||||
{color: processColor('red'), position: 0},
|
||||
{color: processColor('blue'), position: 1},
|
||||
@@ -339,8 +380,10 @@ describe('processBackgroundImage', () => {
|
||||
},
|
||||
];
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].start).toEqual({x: 0.5, y: 0});
|
||||
expect(result[0].end).toEqual({x: 0.5, y: 1});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 180,
|
||||
});
|
||||
});
|
||||
|
||||
it('should process style object with direction enum', () => {
|
||||
@@ -352,8 +395,10 @@ describe('processBackgroundImage', () => {
|
||||
},
|
||||
];
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].start).toEqual({x: 0, y: 0.5});
|
||||
expect(result[0].end).toEqual({x: 1, y: 0.5});
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 90,
|
||||
});
|
||||
});
|
||||
|
||||
it('should process style object with direction angle', () => {
|
||||
@@ -365,10 +410,10 @@ describe('processBackgroundImage', () => {
|
||||
},
|
||||
];
|
||||
const result = processBackgroundImage(input);
|
||||
expect(result[0].start.x).toBeCloseTo(0.146447, 5);
|
||||
expect(result[0].start.y).toBeCloseTo(0.853553, 5);
|
||||
expect(result[0].end.x).toBeCloseTo(0.853553, 5);
|
||||
expect(result[0].end.y).toBeCloseTo(0.146447, 5);
|
||||
expect(result[0].direction).toEqual({
|
||||
type: 'angle',
|
||||
value: 45,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fix up stop positions #1', () => {
|
||||
|
||||
@@ -14,25 +14,28 @@ import type {ProcessedColorValue} from './processColor';
|
||||
import type {GradientValue} from './StyleSheetTypes';
|
||||
|
||||
const processColor = require('./processColor').default;
|
||||
const DIRECTION_REGEX =
|
||||
/^to\s+(?:top|bottom|left|right)(?:\s+(?:top|bottom|left|right))?/;
|
||||
const DIRECTION_KEYWORD_REGEX =
|
||||
/^to\s+(?:top|bottom|left|right)(?:\s+(?:top|bottom|left|right))?/i;
|
||||
const ANGLE_UNIT_REGEX = /^([+-]?\d*\.?\d+)(deg|grad|rad|turn)$/i;
|
||||
|
||||
const TO_BOTTOM_START_END_POINTS = {
|
||||
start: {x: 0.5, y: 0},
|
||||
end: {x: 0.5, y: 1},
|
||||
};
|
||||
type LinearGradientDirection =
|
||||
| {type: 'angle', value: number}
|
||||
| {type: 'keyword', value: string};
|
||||
|
||||
type ParsedGradientValue = {
|
||||
type: 'linearGradient',
|
||||
start: {x: number, y: number},
|
||||
end: {x: number, y: number},
|
||||
direction: LinearGradientDirection,
|
||||
colorStops: $ReadOnlyArray<{
|
||||
color: ProcessedColorValue,
|
||||
position: number,
|
||||
}>,
|
||||
};
|
||||
|
||||
const DEFAULT_DIRECTION: LinearGradientDirection = {
|
||||
type: 'angle',
|
||||
value: 180,
|
||||
};
|
||||
|
||||
export default function processBackgroundImage(
|
||||
backgroundImage: ?($ReadOnlyArray<GradientValue> | string),
|
||||
): $ReadOnlyArray<ParsedGradientValue> {
|
||||
@@ -76,37 +79,43 @@ export default function processBackgroundImage(
|
||||
}
|
||||
}
|
||||
|
||||
let points: {
|
||||
start: ParsedGradientValue['start'],
|
||||
end: ParsedGradientValue['end'],
|
||||
} | null = null;
|
||||
let direction: LinearGradientDirection = DEFAULT_DIRECTION;
|
||||
const bgDirection =
|
||||
bgImage.direction != null ? bgImage.direction.toLowerCase() : null;
|
||||
|
||||
if (typeof bgImage.direction === 'undefined') {
|
||||
points = TO_BOTTOM_START_END_POINTS;
|
||||
} else if (ANGLE_UNIT_REGEX.test(bgImage.direction)) {
|
||||
const angle = parseAngle(bgImage.direction);
|
||||
if (angle != null) {
|
||||
points = calculateStartEndPointsFromAngle(angle);
|
||||
}
|
||||
} else if (DIRECTION_REGEX.test(bgImage.direction)) {
|
||||
const processedPoints = calculateStartEndPointsFromDirection(
|
||||
bgImage.direction,
|
||||
);
|
||||
if (processedPoints != null) {
|
||||
points = processedPoints;
|
||||
if (bgDirection != null) {
|
||||
if (ANGLE_UNIT_REGEX.test(bgDirection)) {
|
||||
const parsedAngle = getAngleInDegrees(bgDirection);
|
||||
if (parsedAngle != null) {
|
||||
direction = {
|
||||
type: 'angle',
|
||||
value: parsedAngle,
|
||||
};
|
||||
} else {
|
||||
// If an angle is invalid, return an empty array and do not apply any gradient. Same as web.
|
||||
return [];
|
||||
}
|
||||
} else if (DIRECTION_KEYWORD_REGEX.test(bgDirection)) {
|
||||
const parsedDirection = getDirectionForKeyword(bgDirection);
|
||||
if (parsedDirection != null) {
|
||||
direction = parsedDirection;
|
||||
} else {
|
||||
// If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
// If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const fixedColorStops = getFixedColorStops(processedColorStops);
|
||||
|
||||
if (points != null) {
|
||||
result = result.concat({
|
||||
type: 'linearGradient',
|
||||
start: points.start,
|
||||
end: points.end,
|
||||
colorStops: fixedColorStops,
|
||||
});
|
||||
}
|
||||
result = result.concat({
|
||||
type: 'linearGradient',
|
||||
direction,
|
||||
colorStops: fixedColorStops,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,30 +127,39 @@ function parseCSSLinearGradient(
|
||||
): $ReadOnlyArray<ParsedGradientValue> {
|
||||
const gradients = [];
|
||||
let match;
|
||||
|
||||
// matches one or more linear-gradient functions in CSS
|
||||
const linearGradientRegex = /linear-gradient\s*\(((?:\([^)]*\)|[^())])*)\)/gi;
|
||||
|
||||
while ((match = linearGradientRegex.exec(cssString))) {
|
||||
const gradientContent = match[1];
|
||||
const parts = gradientContent.split(',');
|
||||
let points = TO_BOTTOM_START_END_POINTS;
|
||||
let direction: LinearGradientDirection = DEFAULT_DIRECTION;
|
||||
const trimmedDirection = parts[0].trim().toLowerCase();
|
||||
|
||||
// matches individual color stops in a gradient function
|
||||
// supports various color formats: named colors, hex colors, rgb(a), and hsl(a)
|
||||
// e.g. "red 20%", "blue 50%", "rgba(0, 0, 0, 0.5) 30% 50%"
|
||||
// TODO: does not support color hint syntax yet. It is WIP.
|
||||
const colorStopRegex =
|
||||
/\s*((?:(?:rgba?|hsla?)\s*\([^)]+\))|#[0-9a-fA-F]+|[a-zA-Z]+)(?:\s+(-?[0-9.]+%?)(?:\s+(-?[0-9.]+%?))?)?\s*/gi;
|
||||
|
||||
if (ANGLE_UNIT_REGEX.test(trimmedDirection)) {
|
||||
const angle = parseAngle(trimmedDirection);
|
||||
if (angle != null) {
|
||||
points = calculateStartEndPointsFromAngle(angle);
|
||||
const parsedAngle = getAngleInDegrees(trimmedDirection);
|
||||
if (parsedAngle != null) {
|
||||
direction = {
|
||||
type: 'angle',
|
||||
value: parsedAngle,
|
||||
};
|
||||
parts.shift();
|
||||
} else {
|
||||
// If an angle is invalid, return an empty array and do not apply any gradient. Same as web.
|
||||
return [];
|
||||
}
|
||||
} else if (DIRECTION_REGEX.test(trimmedDirection)) {
|
||||
const parsedPoints =
|
||||
calculateStartEndPointsFromDirection(trimmedDirection);
|
||||
if (parsedPoints != null) {
|
||||
points = parsedPoints;
|
||||
} else if (DIRECTION_KEYWORD_REGEX.test(trimmedDirection)) {
|
||||
const parsedDirection = getDirectionForKeyword(trimmedDirection);
|
||||
if (parsedDirection != null) {
|
||||
direction = parsedDirection;
|
||||
parts.shift();
|
||||
} else {
|
||||
// If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
|
||||
@@ -198,8 +216,7 @@ function parseCSSLinearGradient(
|
||||
|
||||
gradients.push({
|
||||
type: 'linearGradient',
|
||||
start: points.start,
|
||||
end: points.end,
|
||||
direction,
|
||||
colorStops: fixedColorStops,
|
||||
});
|
||||
}
|
||||
@@ -207,83 +224,43 @@ function parseCSSLinearGradient(
|
||||
return gradients;
|
||||
}
|
||||
|
||||
function calculateStartEndPointsFromDirection(direction: string): ?{
|
||||
start: {x: number, y: number},
|
||||
end: {x: number, y: number},
|
||||
} {
|
||||
function getDirectionForKeyword(direction?: string): ?LinearGradientDirection {
|
||||
if (direction == null) {
|
||||
return null;
|
||||
}
|
||||
// Remove extra whitespace
|
||||
const normalizedDirection = direction.replace(/\s+/g, ' ');
|
||||
const normalized = direction.replace(/\s+/g, ' ').toLowerCase();
|
||||
|
||||
switch (normalizedDirection) {
|
||||
case 'to right':
|
||||
return {
|
||||
start: {x: 0, y: 0.5},
|
||||
end: {x: 1, y: 0.5},
|
||||
};
|
||||
case 'to left':
|
||||
return {
|
||||
start: {x: 1, y: 0.5},
|
||||
end: {x: 0, y: 0.5},
|
||||
};
|
||||
case 'to bottom':
|
||||
return TO_BOTTOM_START_END_POINTS;
|
||||
switch (normalized) {
|
||||
case 'to top':
|
||||
return {
|
||||
start: {x: 0.5, y: 1},
|
||||
end: {x: 0.5, y: 0},
|
||||
};
|
||||
case 'to bottom right':
|
||||
case 'to right bottom':
|
||||
return {
|
||||
start: {x: 0, y: 0},
|
||||
end: {x: 1, y: 1},
|
||||
};
|
||||
case 'to top left':
|
||||
case 'to left top':
|
||||
return {
|
||||
start: {x: 1, y: 1},
|
||||
end: {x: 0, y: 0},
|
||||
};
|
||||
case 'to bottom left':
|
||||
case 'to left bottom':
|
||||
return {
|
||||
start: {x: 1, y: 0},
|
||||
end: {x: 0, y: 1},
|
||||
};
|
||||
return {type: 'angle', value: 0};
|
||||
case 'to right':
|
||||
return {type: 'angle', value: 90};
|
||||
case 'to bottom':
|
||||
return {type: 'angle', value: 180};
|
||||
case 'to left':
|
||||
return {type: 'angle', value: 270};
|
||||
case 'to top right':
|
||||
case 'to right top':
|
||||
return {
|
||||
start: {x: 0, y: 1},
|
||||
end: {x: 1, y: 0},
|
||||
};
|
||||
return {type: 'keyword', value: 'to top right'};
|
||||
case 'to bottom right':
|
||||
case 'to right bottom':
|
||||
return {type: 'keyword', value: 'to bottom right'};
|
||||
case 'to top left':
|
||||
case 'to left top':
|
||||
return {type: 'keyword', value: 'to top left'};
|
||||
case 'to bottom left':
|
||||
case 'to left bottom':
|
||||
return {type: 'keyword', value: 'to bottom left'};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateStartEndPointsFromAngle(angleRadians: number): {
|
||||
start: {x: number, y: number},
|
||||
end: {x: number, y: number},
|
||||
} {
|
||||
// Normalize angle to be between 0 and 2π
|
||||
let angleRadiansNormalized = angleRadians % (2 * Math.PI);
|
||||
if (angleRadiansNormalized < 0) {
|
||||
angleRadiansNormalized += 2 * Math.PI;
|
||||
function getAngleInDegrees(angle?: string): ?number {
|
||||
if (angle == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const endX = 0.5 + 0.5 * Math.sin(angleRadiansNormalized);
|
||||
const endY = 0.5 - 0.5 * Math.cos(angleRadiansNormalized);
|
||||
|
||||
const startX = 1 - endX;
|
||||
const startY = 1 - endY;
|
||||
|
||||
return {
|
||||
start: {x: startX, y: startY},
|
||||
end: {x: endX, y: endY},
|
||||
};
|
||||
}
|
||||
|
||||
function parseAngle(angle: string): ?number {
|
||||
const match = angle.match(ANGLE_UNIT_REGEX);
|
||||
if (!match) {
|
||||
return null;
|
||||
@@ -294,13 +271,13 @@ function parseAngle(angle: string): ?number {
|
||||
const numericValue = parseFloat(value);
|
||||
switch (unit) {
|
||||
case 'deg':
|
||||
return (numericValue * Math.PI) / 180;
|
||||
case 'grad':
|
||||
return (numericValue * Math.PI) / 200;
|
||||
case 'rad':
|
||||
return numericValue;
|
||||
case 'grad':
|
||||
return numericValue * 0.9; // 1 grad = 0.9 degrees
|
||||
case 'rad':
|
||||
return (numericValue * 180) / Math.PI;
|
||||
case 'turn':
|
||||
return numericValue * 2 * Math.PI;
|
||||
return numericValue * 360; // 1 turn = 360 degrees
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ const NativeModules = require('../BatchedBridge/NativeModules');
|
||||
|
||||
const turboModuleProxy = global.__turboModuleProxy;
|
||||
|
||||
const useLegacyNativeModuleInterop =
|
||||
global.RN$Bridgeless !== true || global.RN$TurboInterop === true;
|
||||
|
||||
function requireModule<T: TurboModule>(name: string): ?T {
|
||||
if (turboModuleProxy != null) {
|
||||
const module: ?T = turboModuleProxy(name);
|
||||
@@ -27,8 +24,11 @@ function requireModule<T: TurboModule>(name: string): ?T {
|
||||
}
|
||||
}
|
||||
|
||||
if (useLegacyNativeModuleInterop) {
|
||||
// Backward compatibility layer during migration.
|
||||
if (
|
||||
global.RN$Bridgeless !== true ||
|
||||
global.RN$TurboInterop === true ||
|
||||
global.RN$UnifiedNativeModuleProxy === true
|
||||
) {
|
||||
const legacyModule: ?T = NativeModules[name];
|
||||
if (legacyModule != null) {
|
||||
return legacyModule;
|
||||
|
||||
+3
-1
@@ -105,7 +105,9 @@ export function setColorScheme(colorScheme: ?ColorSchemeName): void {
|
||||
const {NativeAppearance} = state;
|
||||
if (NativeAppearance != null) {
|
||||
NativeAppearance.setColorScheme(colorScheme ?? 'unspecified');
|
||||
state.appearance = {colorScheme};
|
||||
state.appearance = {
|
||||
colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import useColorScheme from '../useColorScheme';
|
||||
|
||||
describe('useColorScheme', () => {
|
||||
it('should return a mocked light theme by default', () => {
|
||||
expect(jest.isMockFunction(useColorScheme)).toBe(true);
|
||||
expect(useColorScheme()).toBe('light');
|
||||
});
|
||||
|
||||
it('should have console.error when not using mock', () => {
|
||||
const useColorSchemeActual = jest.requireActual<{
|
||||
default: typeof useColorScheme,
|
||||
}>('../useColorScheme').default;
|
||||
const spy = jest.spyOn(console, 'error').mockImplementationOnce(() => {
|
||||
// Simulate LogBox console.error() call to throw an error and stop the further execution
|
||||
throw new Error('console.error() was called');
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
useColorSchemeActual();
|
||||
}).toThrow();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
expect.stringMatching(
|
||||
/Invalid hook call. Hooks can only be called inside of the body of a function component./,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -5609,12 +5609,7 @@ declare function create(
|
||||
declare const Presets: {
|
||||
easeInEaseOut: LayoutAnimationConfig,
|
||||
linear: LayoutAnimationConfig,
|
||||
spring: {
|
||||
duration: 700,
|
||||
create: { type: \\"linear\\", property: \\"opacity\\" },
|
||||
update: { type: \\"spring\\", springDamping: 0.4 },
|
||||
delete: { type: \\"linear\\", property: \\"opacity\\" },
|
||||
},
|
||||
spring: LayoutAnimationConfig,
|
||||
};
|
||||
declare const LayoutAnimation: {
|
||||
configureNext: configureNext,
|
||||
@@ -6903,49 +6898,6 @@ exports[`public API should not change unintentionally Libraries/Network/fetch.js
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/components/Colors.js 1`] = `
|
||||
"declare export default {
|
||||
primary: \\"#1292B4\\",
|
||||
white: \\"#FFF\\",
|
||||
lighter: \\"#F3F3F3\\",
|
||||
light: \\"#DAE1E7\\",
|
||||
dark: \\"#444\\",
|
||||
darker: \\"#222\\",
|
||||
black: \\"#000\\",
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/components/DebugInstructions.js 1`] = `
|
||||
"declare const DebugInstructions: () => Node;
|
||||
declare export default typeof DebugInstructions;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/components/Header.js 1`] = `
|
||||
"declare const Header: () => Node;
|
||||
declare export default typeof Header;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/components/HermesBadge.js 1`] = `
|
||||
"declare const HermesBadge: () => Node;
|
||||
declare export default typeof HermesBadge;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/components/LearnMoreLinks.js 1`] = `
|
||||
"declare const LinkList: () => Node;
|
||||
declare export default typeof LinkList;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/components/ReloadInstructions.js 1`] = `
|
||||
"declare const ReloadInstructions: () => Node;
|
||||
declare export default typeof ReloadInstructions;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/NewAppScreen/index.js 1`] = `
|
||||
"export {
|
||||
Colors,
|
||||
@@ -8371,10 +8323,12 @@ declare module.exports: processAspectRatio;
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/StyleSheet/processBackgroundImage.js 1`] = `
|
||||
"type ParsedGradientValue = {
|
||||
"type LinearGradientDirection =
|
||||
| { type: \\"angle\\", value: number }
|
||||
| { type: \\"keyword\\", value: string };
|
||||
type ParsedGradientValue = {
|
||||
type: \\"linearGradient\\",
|
||||
start: { x: number, y: number },
|
||||
end: { x: number, y: number },
|
||||
direction: LinearGradientDirection,
|
||||
colorStops: $ReadOnlyArray<{
|
||||
color: ProcessedColorValue,
|
||||
position: number,
|
||||
|
||||
@@ -26,6 +26,7 @@ const IGNORE_PATTERNS = [
|
||||
'**/*.fb.js',
|
||||
'**/*.macos.js',
|
||||
'**/*.windows.js',
|
||||
'Libraries/NewAppScreen/components/**',
|
||||
// Non source files
|
||||
'Libraries/Renderer/implementations/**',
|
||||
'Libraries/Renderer/shims/**',
|
||||
|
||||
+40
-11
@@ -67,28 +67,52 @@
|
||||
* ScrollView, we force it to be centered, but when you zoom or the content otherwise
|
||||
* becomes larger than the ScrollView, there is no padding around the content but it
|
||||
* can still fill the whole view.
|
||||
* This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/.
|
||||
*/
|
||||
- (void)centerContentIfNeeded
|
||||
{
|
||||
if (!_centerContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGSize contentSize = self.contentSize;
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
if (CGSizeEqualToSize(contentSize, CGSizeZero) || CGSizeEqualToSize(boundsSize, CGSizeZero)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat top = 0, left = 0;
|
||||
if (contentSize.width < boundsSize.width) {
|
||||
left = (boundsSize.width - contentSize.width) * 0.5f;
|
||||
}
|
||||
if (contentSize.height < boundsSize.height) {
|
||||
top = (boundsSize.height - contentSize.height) * 0.5f;
|
||||
}
|
||||
self.contentInset = UIEdgeInsetsMake(top, left, top, left);
|
||||
}
|
||||
|
||||
- (void)setContentOffset:(CGPoint)contentOffset
|
||||
{
|
||||
if (_isSetContentOffsetDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_centerContent && !CGSizeEqualToSize(self.contentSize, CGSizeZero)) {
|
||||
CGSize scrollViewSize = self.bounds.size;
|
||||
if (self.contentSize.width <= scrollViewSize.width) {
|
||||
contentOffset.x = -(scrollViewSize.width - self.contentSize.width) / 2.0;
|
||||
}
|
||||
if (self.contentSize.height <= scrollViewSize.height) {
|
||||
contentOffset.y = -(scrollViewSize.height - self.contentSize.height) / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
super.contentOffset = CGPointMake(
|
||||
RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"),
|
||||
RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y"));
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
[self centerContentIfNeeded];
|
||||
}
|
||||
|
||||
- (void)didAddSubview:(UIView *)subview
|
||||
{
|
||||
[super didAddSubview:subview];
|
||||
[self centerContentIfNeeded];
|
||||
}
|
||||
|
||||
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
|
||||
{
|
||||
if ([_overridingDelegate respondsToSelector:@selector(touchesShouldCancelInContentView:)]) {
|
||||
@@ -258,6 +282,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidZoom:(__unused UIScrollView *)scrollView
|
||||
{
|
||||
[self centerContentIfNeeded];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)isHorizontal:(UIScrollView *)scrollView
|
||||
|
||||
+7
-2
@@ -450,10 +450,15 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
||||
|
||||
- (void)textInputDidChangeSelection
|
||||
{
|
||||
[self _updateTypingAttributes];
|
||||
if (_comingFromJS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// T207198334: Setting a new AttributedString (_comingFromJS) will trigger a selection change before the backing
|
||||
// string is updated, so indicies won't point to what we want yet. Only respond to user selection change, and let
|
||||
// `_setAttributedString` handle updating typing attributes if content changes.
|
||||
[self _updateTypingAttributes];
|
||||
|
||||
const auto &props = static_cast<const TextInputProps &>(*_props);
|
||||
if (props.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
||||
[self textInputDidChange];
|
||||
@@ -722,7 +727,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
||||
// https://github.com/facebook/react-native/blob/3102a58df38d96f3dacef0530e4dbb399037fcd2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/SetSpanOperation.kt#L30
|
||||
- (void)_updateTypingAttributes
|
||||
{
|
||||
if (_backedTextInputView.attributedText.length > 0) {
|
||||
if (_backedTextInputView.attributedText.length > 0 && _backedTextInputView.selectedTextRange != nil) {
|
||||
NSUInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
||||
toPosition:_backedTextInputView.selectedTextRange.start];
|
||||
|
||||
|
||||
+35
-48
@@ -10,11 +10,13 @@
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <ranges>
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBorderDrawing.h>
|
||||
#import <React/RCTBoxShadow.h>
|
||||
#import <React/RCTConversions.h>
|
||||
#import <React/RCTLinearGradient.h>
|
||||
#import <React/RCTLocalizedString.h>
|
||||
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
#import <react/renderer/components/view/ViewComponentDescriptor.h>
|
||||
@@ -38,7 +40,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
||||
CALayer *_outlineLayer;
|
||||
CALayer *_boxShadowLayer;
|
||||
CALayer *_filterLayer;
|
||||
NSMutableArray<CAGradientLayer *> *_gradientLayers;
|
||||
NSMutableArray<CALayer *> *_backgroundImageLayers;
|
||||
BOOL _needsInvalidateLayer;
|
||||
BOOL _isJSResponder;
|
||||
BOOL _removeClippedSubviews;
|
||||
@@ -1001,50 +1003,35 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
}
|
||||
|
||||
// background image
|
||||
[self clearExistingGradientLayers];
|
||||
[self clearExistingBackgroundImageLayers];
|
||||
if (!_props->backgroundImage.empty()) {
|
||||
for (const auto &gradient : _props->backgroundImage) {
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
NSMutableArray *colors = [NSMutableArray array];
|
||||
NSMutableArray *locations = [NSMutableArray array];
|
||||
for (const auto &colorStop : gradient.colorStops) {
|
||||
if (colorStop.position.has_value()) {
|
||||
auto location = @(colorStop.position.value());
|
||||
UIColor *color = RCTUIColorFromSharedColor(colorStop.color);
|
||||
[colors addObject:(id)color.CGColor];
|
||||
[locations addObject:location];
|
||||
// iterate in reverse to match CSS specification
|
||||
for (const auto &backgroundImage : std::ranges::reverse_view(_props->backgroundImage)) {
|
||||
if (std::holds_alternative<LinearGradient>(backgroundImage)) {
|
||||
const auto &linearGradient = std::get<LinearGradient>(backgroundImage);
|
||||
CALayer *backgroundImageLayer = [RCTLinearGradient gradientLayerWithSize:self.layer.bounds.size
|
||||
gradient:linearGradient];
|
||||
backgroundImageLayer.frame = layer.bounds;
|
||||
backgroundImageLayer.masksToBounds = YES;
|
||||
// To make border radius work with gradient layers
|
||||
if (borderMetrics.borderRadii.isUniform()) {
|
||||
backgroundImageLayer.cornerRadius = layer.cornerRadius;
|
||||
backgroundImageLayer.cornerCurve = layer.cornerCurve;
|
||||
backgroundImageLayer.mask = nil;
|
||||
} else {
|
||||
CAShapeLayer *maskLayer =
|
||||
[self createMaskLayer:self.bounds
|
||||
cornerInsets:RCTGetCornerInsets(
|
||||
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero)];
|
||||
backgroundImageLayer.mask = maskLayer;
|
||||
backgroundImageLayer.cornerRadius = 0;
|
||||
}
|
||||
|
||||
backgroundImageLayer.zPosition = BACKGROUND_COLOR_ZPOSITION;
|
||||
|
||||
[self.layer addSublayer:backgroundImageLayer];
|
||||
[_backgroundImageLayers addObject:backgroundImageLayer];
|
||||
}
|
||||
gradientLayer.startPoint = CGPointMake(gradient.startX, gradient.startY);
|
||||
gradientLayer.endPoint = CGPointMake(gradient.endX, gradient.endY);
|
||||
|
||||
if (locations.count > 0) {
|
||||
gradientLayer.locations = locations;
|
||||
}
|
||||
gradientLayer.colors = colors;
|
||||
gradientLayer.frame = layer.bounds;
|
||||
|
||||
// border styling to work with gradient layers
|
||||
if (useCoreAnimationBorderRendering) {
|
||||
gradientLayer.borderWidth = layer.borderWidth;
|
||||
gradientLayer.borderColor = layer.borderColor;
|
||||
gradientLayer.cornerRadius = layer.cornerRadius;
|
||||
gradientLayer.cornerCurve = layer.cornerCurve;
|
||||
} else {
|
||||
CAShapeLayer *maskLayer = [CAShapeLayer layer];
|
||||
CGPathRef path = RCTPathCreateWithRoundedRect(
|
||||
self.bounds,
|
||||
RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero),
|
||||
nil);
|
||||
maskLayer.path = path;
|
||||
CGPathRelease(path);
|
||||
gradientLayer.mask = maskLayer;
|
||||
}
|
||||
|
||||
gradientLayer.zPosition = BACKGROUND_COLOR_ZPOSITION;
|
||||
|
||||
[self.layer addSublayer:gradientLayer];
|
||||
[_gradientLayers addObject:gradientLayer];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1115,16 +1102,16 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
return maskLayer;
|
||||
}
|
||||
|
||||
- (void)clearExistingGradientLayers
|
||||
- (void)clearExistingBackgroundImageLayers
|
||||
{
|
||||
if (_gradientLayers == nil) {
|
||||
_gradientLayers = [NSMutableArray new];
|
||||
if (_backgroundImageLayers == nil) {
|
||||
_backgroundImageLayers = [NSMutableArray new];
|
||||
return;
|
||||
}
|
||||
for (CAGradientLayer *gradientLayer in _gradientLayers) {
|
||||
[gradientLayer removeFromSuperlayer];
|
||||
for (CALayer *backgroundImageLayer in _backgroundImageLayers) {
|
||||
[backgroundImageLayer removeFromSuperlayer];
|
||||
}
|
||||
[_gradientLayers removeAllObjects];
|
||||
[_backgroundImageLayers removeAllObjects];
|
||||
}
|
||||
|
||||
#pragma mark - Accessibility
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <react/renderer/components/view/ViewProps.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTLinearGradient : NSObject
|
||||
|
||||
+ (CALayer *)gradientLayerWithSize:(CGSize)size gradient:(const facebook::react::LinearGradient &)gradient;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTLinearGradient.h"
|
||||
|
||||
#import <React/RCTConversions.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
@implementation RCTLinearGradient
|
||||
|
||||
+ (CALayer *)gradientLayerWithSize:(CGSize)size gradient:(const LinearGradient &)gradient
|
||||
{
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
|
||||
const auto &direction = gradient.direction;
|
||||
const auto &colorStops = gradient.colorStops;
|
||||
|
||||
UIImage *gradientImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||
CGContextRef context = rendererContext.CGContext;
|
||||
NSMutableArray *colors = [NSMutableArray array];
|
||||
CGFloat locations[colorStops.size()];
|
||||
|
||||
for (size_t i = 0; i < colorStops.size(); ++i) {
|
||||
const auto &colorStop = colorStops[i];
|
||||
CGColorRef cgColor = RCTCreateCGColorRefFromSharedColor(colorStop.color);
|
||||
[colors addObject:(__bridge id)cgColor];
|
||||
locations[i] = colorStop.position;
|
||||
}
|
||||
|
||||
CGGradientRef cgGradient = CGGradientCreateWithColors(NULL, (__bridge CFArrayRef)colors, locations);
|
||||
|
||||
CGPoint startPoint;
|
||||
CGPoint endPoint;
|
||||
|
||||
if (direction.type == GradientDirectionType::Angle) {
|
||||
CGFloat angle = std::get<Float>(direction.value);
|
||||
std::tie(startPoint, endPoint) = getPointsFromAngle(angle, size);
|
||||
} else if (direction.type == GradientDirectionType::Keyword) {
|
||||
auto keyword = std::get<GradientKeyword>(direction.value);
|
||||
CGFloat angle = getAngleForKeyword(keyword, size);
|
||||
std::tie(startPoint, endPoint) = getPointsFromAngle(angle, size);
|
||||
} else {
|
||||
// Default to top-to-bottom gradient
|
||||
startPoint = CGPointMake(0.0, 0.0);
|
||||
endPoint = CGPointMake(0.0, size.height);
|
||||
}
|
||||
|
||||
CGContextDrawLinearGradient(context, cgGradient, startPoint, endPoint, 0);
|
||||
|
||||
for (id color in colors) {
|
||||
CGColorRelease((__bridge CGColorRef)color);
|
||||
}
|
||||
CGGradientRelease(cgGradient);
|
||||
}];
|
||||
|
||||
CALayer *gradientLayer = [CALayer layer];
|
||||
gradientLayer.contents = (__bridge id)gradientImage.CGImage;
|
||||
|
||||
return gradientLayer;
|
||||
}
|
||||
|
||||
// Spec: https://www.w3.org/TR/css-images-3/#linear-gradient-syntax
|
||||
// Reference:
|
||||
// https://github.com/chromium/chromium/blob/d32abbe13f5d52be7127fe25d5b778498165fab8/third_party/blink/renderer/core/css/css_gradient_value.cc#L1057
|
||||
static std::pair<CGPoint, CGPoint> getPointsFromAngle(CGFloat angle, CGSize size)
|
||||
{
|
||||
angle = fmod(angle, 360.0);
|
||||
if (angle < 0) {
|
||||
angle += 360.0;
|
||||
}
|
||||
|
||||
if (angle == 0.0) {
|
||||
return {CGPointMake(0, size.height), CGPointMake(0, 0)};
|
||||
}
|
||||
if (angle == 90.0) {
|
||||
return {CGPointMake(0, 0), CGPointMake(size.width, 0)};
|
||||
}
|
||||
if (angle == 180.0) {
|
||||
return {CGPointMake(0, 0), CGPointMake(0, size.height)};
|
||||
}
|
||||
if (angle == 270.0) {
|
||||
return {CGPointMake(size.width, 0), CGPointMake(0, 0)};
|
||||
}
|
||||
|
||||
CGFloat radians = (90 - angle) * M_PI / 180.0;
|
||||
CGFloat slope = tan(radians);
|
||||
CGFloat perpendicularSlope = -1 / slope;
|
||||
|
||||
CGFloat halfHeight = size.height / 2;
|
||||
CGFloat halfWidth = size.width / 2;
|
||||
|
||||
CGPoint endCorner;
|
||||
if (angle < 90) {
|
||||
endCorner = CGPointMake(halfWidth, halfHeight);
|
||||
} else if (angle < 180) {
|
||||
endCorner = CGPointMake(halfWidth, -halfHeight);
|
||||
} else if (angle < 270) {
|
||||
endCorner = CGPointMake(-halfWidth, -halfHeight);
|
||||
} else {
|
||||
endCorner = CGPointMake(-halfWidth, halfHeight);
|
||||
}
|
||||
|
||||
CGFloat c = endCorner.y - perpendicularSlope * endCorner.x;
|
||||
CGFloat endX = c / (slope - perpendicularSlope);
|
||||
CGFloat endY = perpendicularSlope * endX + c;
|
||||
|
||||
return {CGPointMake(halfWidth - endX, halfHeight + endY), CGPointMake(halfWidth + endX, halfHeight - endY)};
|
||||
}
|
||||
|
||||
// Spec: https://www.w3.org/TR/css-images-3/#linear-gradient-syntax
|
||||
// Refer `using keywords` section
|
||||
static CGFloat getAngleForKeyword(GradientKeyword keyword, CGSize size)
|
||||
{
|
||||
switch (keyword) {
|
||||
case GradientKeyword::ToTopRight: {
|
||||
CGFloat angleDeg = atan(size.width / size.height) * 180.0 / M_PI;
|
||||
return 90.0 - angleDeg;
|
||||
}
|
||||
case GradientKeyword::ToBottomRight:
|
||||
return atan(size.width / size.height) * 180.0 / M_PI + 90.0;
|
||||
case GradientKeyword::ToTopLeft:
|
||||
return atan(size.width / size.height) * 180.0 / M_PI + 270.0;
|
||||
case GradientKeyword::ToBottomLeft:
|
||||
return atan(size.height / size.width) * 180.0 / M_PI + 180.0;
|
||||
default:
|
||||
return 180.0;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -121,8 +121,8 @@ using namespace facebook::react;
|
||||
auto &props = *sharedProps;
|
||||
props.layoutConstraints = LayoutConstraints{{0, 0}, {500, 500}};
|
||||
auto &yogaStyle = props.yogaStyle;
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
|
||||
return sharedProps;
|
||||
})
|
||||
.children({
|
||||
@@ -136,8 +136,8 @@ using namespace facebook::react;
|
||||
yogaStyle.setPositionType(yoga::PositionType::Absolute);
|
||||
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
|
||||
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(0));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
|
||||
return sharedProps;
|
||||
})
|
||||
.children({
|
||||
@@ -216,8 +216,8 @@ using namespace facebook::react;
|
||||
yogaStyle.setPositionType(yoga::PositionType::Absolute);
|
||||
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
|
||||
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(30));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(50));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50));
|
||||
return sharedProps;
|
||||
})
|
||||
.children({
|
||||
@@ -260,8 +260,8 @@ using namespace facebook::react;
|
||||
yogaStyle.setPositionType(yoga::PositionType::Absolute);
|
||||
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
|
||||
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(90));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(50));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50));
|
||||
return sharedProps;
|
||||
})
|
||||
.children({
|
||||
@@ -418,8 +418,8 @@ static ParagraphShadowNode::ConcreteState::Shared stateWithShadowNode(
|
||||
auto &props = *sharedProps;
|
||||
props.layoutConstraints = LayoutConstraints{{0, 0}, {500, 500}};
|
||||
auto &yogaStyle = props.yogaStyle;
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
|
||||
return sharedProps;
|
||||
})
|
||||
.children({
|
||||
@@ -434,8 +434,8 @@ static ParagraphShadowNode::ConcreteState::Shared stateWithShadowNode(
|
||||
yogaStyle.setPositionType(yoga::PositionType::Absolute);
|
||||
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
|
||||
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(90));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(20));
|
||||
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
|
||||
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20));
|
||||
return sharedProps;
|
||||
})
|
||||
.children({
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import <React/RCTConvert.h>
|
||||
|
||||
typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescription);
|
||||
typedef CGFloat RCTFontWeight;
|
||||
|
||||
/**
|
||||
* React Native will use the System font for rendering by default. If you want to
|
||||
@@ -19,6 +20,7 @@ typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescrip
|
||||
*/
|
||||
RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler);
|
||||
RCT_EXTERN BOOL RCTHasFontHandlerSet(void);
|
||||
RCT_EXTERN RCTFontWeight RCTGetFontWeight(UIFont *font);
|
||||
|
||||
@interface RCTFont : NSObject
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
typedef CGFloat RCTFontWeight;
|
||||
static RCTFontWeight weightOfFont(UIFont *font)
|
||||
RCTFontWeight RCTGetFontWeight(UIFont *font)
|
||||
{
|
||||
static NSArray<NSString *> *weightSuffixes;
|
||||
static NSArray<NSNumber *> *fontWeights;
|
||||
@@ -405,7 +404,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
|
||||
if (font) {
|
||||
familyName = font.familyName ?: defaultFontFamily;
|
||||
fontSize = font.pointSize ?: defaultFontSize;
|
||||
fontWeight = weightOfFont(font);
|
||||
fontWeight = RCTGetFontWeight(font);
|
||||
isItalic = isItalicFont(font);
|
||||
isCondensed = isCondensedFont(font);
|
||||
}
|
||||
@@ -453,7 +452,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
|
||||
// It's actually a font name, not a font family name,
|
||||
// but we'll do what was meant, not what was said.
|
||||
familyName = font.familyName;
|
||||
fontWeight = weight ? fontWeight : weightOfFont(font);
|
||||
fontWeight = weight ? fontWeight : RCTGetFontWeight(font);
|
||||
isItalic = style ? isItalic : isItalicFont(font);
|
||||
isCondensed = isCondensedFont(font);
|
||||
} else {
|
||||
@@ -476,7 +475,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
|
||||
for (NSString *name in names) {
|
||||
UIFont *match = [UIFont fontWithName:name size:fontSize];
|
||||
if (isItalic == isItalicFont(match) && isCondensed == isCondensedFont(match)) {
|
||||
CGFloat testWeight = weightOfFont(match);
|
||||
CGFloat testWeight = RCTGetFontWeight(match);
|
||||
if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
|
||||
font = match;
|
||||
closestWeight = testWeight;
|
||||
|
||||
@@ -87,9 +87,6 @@ CGFloat RCTCoreGraphicsFloatFromYogaValue(YGValue value, CGFloat baseFloatValue)
|
||||
return RCTCoreGraphicsFloatFromYogaFloat(value.value) * baseFloatValue;
|
||||
case YGUnitAuto:
|
||||
case YGUnitUndefined:
|
||||
case YGUnitMaxContent:
|
||||
case YGUnitFitContent:
|
||||
case YGUnitStretch:
|
||||
return baseFloatValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,6 @@ typedef NS_ENUM(unsigned int, meta_prop_t) {
|
||||
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \
|
||||
switch (ygvalue.unit) { \
|
||||
case YGUnitAuto: \
|
||||
case YGUnitMaxContent: \
|
||||
case YGUnitFitContent: \
|
||||
case YGUnitStretch: \
|
||||
case YGUnitUndefined: \
|
||||
setter(__VA_ARGS__, YGUndefined); \
|
||||
break; \
|
||||
@@ -91,35 +88,6 @@ typedef NS_ENUM(unsigned int, meta_prop_t) {
|
||||
case YGUnitPercent: \
|
||||
setter##Percent(__VA_ARGS__, ygvalue.value); \
|
||||
break; \
|
||||
case YGUnitMaxContent: \
|
||||
case YGUnitFitContent: \
|
||||
case YGUnitStretch: \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define RCT_SET_YGVALUE_AUTO_INTRINSIC(ygvalue, setter, ...) \
|
||||
switch (ygvalue.unit) { \
|
||||
case YGUnitAuto: \
|
||||
setter##Auto(__VA_ARGS__); \
|
||||
break; \
|
||||
case YGUnitMaxContent: \
|
||||
setter##MaxContent(__VA_ARGS__); \
|
||||
break; \
|
||||
case YGUnitFitContent: \
|
||||
setter##FitContent(__VA_ARGS__); \
|
||||
break; \
|
||||
case YGUnitStretch: \
|
||||
setter##Stretch(__VA_ARGS__); \
|
||||
break; \
|
||||
case YGUnitUndefined: \
|
||||
setter(__VA_ARGS__, YGUndefined); \
|
||||
break; \
|
||||
case YGUnitPoint: \
|
||||
setter(__VA_ARGS__, ygvalue.value); \
|
||||
break; \
|
||||
case YGUnitPercent: \
|
||||
setter##Percent(__VA_ARGS__, ygvalue.value); \
|
||||
break; \
|
||||
}
|
||||
|
||||
static void RCTProcessMetaPropsPadding(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
|
||||
@@ -515,14 +483,14 @@ RCT_BORDER_PROPERTY(Start, START)
|
||||
RCT_BORDER_PROPERTY(End, END)
|
||||
|
||||
// Dimensions
|
||||
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
|
||||
-(void)set##setProp : (YGValue)value \
|
||||
{ \
|
||||
RCT_SET_YGVALUE_AUTO_INTRINSIC(value, YGNodeStyleSet##cssProp, _yogaNode); \
|
||||
} \
|
||||
-(YGValue)getProp \
|
||||
{ \
|
||||
return YGNodeStyleGet##cssProp(_yogaNode); \
|
||||
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
|
||||
-(void)set##setProp : (YGValue)value \
|
||||
{ \
|
||||
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSet##cssProp, _yogaNode); \
|
||||
} \
|
||||
-(YGValue)getProp \
|
||||
{ \
|
||||
return YGNodeStyleGet##cssProp(_yogaNode); \
|
||||
}
|
||||
|
||||
#define RCT_MIN_MAX_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
|
||||
@@ -666,7 +634,7 @@ RCTShadowViewMeasure(YGNodeConstRef node, float width, YGMeasureMode widthMode,
|
||||
|
||||
- (void)setFlexBasis:(YGValue)value
|
||||
{
|
||||
RCT_SET_YGVALUE_AUTO_INTRINSIC(value, YGNodeStyleSetFlexBasis, _yogaNode);
|
||||
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSetFlexBasis, _yogaNode);
|
||||
}
|
||||
|
||||
- (YGValue)flexBasis
|
||||
|
||||
@@ -159,26 +159,8 @@
|
||||
return !shouldDisableScrollInteraction;
|
||||
}
|
||||
|
||||
/*
|
||||
* Automatically centers the content such that if the content is smaller than the
|
||||
* ScrollView, we force it to be centered, but when you zoom or the content otherwise
|
||||
* becomes larger than the ScrollView, there is no padding around the content but it
|
||||
* can still fill the whole view.
|
||||
*/
|
||||
- (void)setContentOffset:(CGPoint)contentOffset
|
||||
{
|
||||
UIView *contentView = [self contentView];
|
||||
if (contentView && _centerContent && !CGSizeEqualToSize(contentView.frame.size, CGSizeZero)) {
|
||||
CGSize subviewSize = contentView.frame.size;
|
||||
CGSize scrollViewSize = self.bounds.size;
|
||||
if (subviewSize.width <= scrollViewSize.width) {
|
||||
contentOffset.x = -(scrollViewSize.width - subviewSize.width) / 2.0;
|
||||
}
|
||||
if (subviewSize.height <= scrollViewSize.height) {
|
||||
contentOffset.y = -(scrollViewSize.height - subviewSize.height) / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
super.contentOffset = CGPointMake(
|
||||
RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"),
|
||||
RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y"));
|
||||
@@ -433,6 +415,12 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
[self centerContentIfNeeded];
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[super insertReactSubview:view atIndex:atIndex];
|
||||
@@ -450,6 +438,8 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(
|
||||
RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection);
|
||||
[_scrollView addSubview:view];
|
||||
}
|
||||
|
||||
[self centerContentIfNeeded];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(UIView *)subview
|
||||
@@ -658,9 +648,45 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(
|
||||
}
|
||||
|
||||
RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin)
|
||||
RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
|
||||
RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
|
||||
|
||||
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
|
||||
{
|
||||
[self centerContentIfNeeded];
|
||||
|
||||
RCT_SEND_SCROLL_EVENT(onScroll, nil);
|
||||
RCT_FORWARD_SCROLL_EVENT(scrollViewDidZoom : scrollView);
|
||||
}
|
||||
|
||||
/*
|
||||
* Automatically centers the content such that if the content is smaller than the
|
||||
* ScrollView, we force it to be centered, but when you zoom or the content otherwise
|
||||
* becomes larger than the ScrollView, there is no padding around the content but it
|
||||
* can still fill the whole view.
|
||||
* This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/.
|
||||
*/
|
||||
- (void)centerContentIfNeeded
|
||||
{
|
||||
if (!_scrollView.centerContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGSize contentSize = self.contentSize;
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
if (CGSizeEqualToSize(contentSize, CGSizeZero) || CGSizeEqualToSize(boundsSize, CGSizeZero)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat top = 0, left = 0;
|
||||
if (contentSize.width < boundsSize.width) {
|
||||
left = (boundsSize.width - contentSize.width) * 0.5f;
|
||||
}
|
||||
if (contentSize.height < boundsSize.height) {
|
||||
top = (boundsSize.height - contentSize.height) * 0.5f;
|
||||
}
|
||||
_scrollView.contentInset = UIEdgeInsetsMake(top, left, top, left);
|
||||
}
|
||||
|
||||
- (void)addScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener
|
||||
{
|
||||
[_scrollListeners addObject:scrollListener];
|
||||
@@ -939,6 +965,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
|
||||
CGSize contentSize = self.contentSize;
|
||||
if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) {
|
||||
_scrollView.contentSize = contentSize;
|
||||
[self centerContentIfNeeded];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -342,6 +342,7 @@ public abstract class com/facebook/react/ReactNativeHost {
|
||||
public abstract interface class com/facebook/react/ReactPackage {
|
||||
public abstract fun createNativeModules (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/List;
|
||||
public abstract fun createViewManagers (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/List;
|
||||
public fun getModule (Ljava/lang/String;Lcom/facebook/react/bridge/ReactApplicationContext;)Lcom/facebook/react/bridge/NativeModule;
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/ReactPackageLogger {
|
||||
@@ -376,6 +377,7 @@ public class com/facebook/react/ReactRootView : android/widget/FrameLayout, com/
|
||||
protected fun dispatchJSPointerEvent (Landroid/view/MotionEvent;Z)V
|
||||
protected fun dispatchJSTouchEvent (Landroid/view/MotionEvent;)V
|
||||
public fun dispatchKeyEvent (Landroid/view/KeyEvent;)Z
|
||||
protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z
|
||||
protected fun finalize ()V
|
||||
public fun getAppProperties ()Landroid/os/Bundle;
|
||||
public fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
|
||||
@@ -424,10 +426,6 @@ public abstract interface class com/facebook/react/ReactRootView$ReactRootViewEv
|
||||
public abstract fun onAttachedToReactInstance (Lcom/facebook/react/ReactRootView;)V
|
||||
}
|
||||
|
||||
public abstract class com/facebook/react/TurboReactPackage : com/facebook/react/BaseReactPackage {
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/ViewManagerOnDemandReactPackage {
|
||||
public abstract fun createViewManager (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/lang/String;)Lcom/facebook/react/uimanager/ViewManager;
|
||||
public abstract fun getViewManagerNames (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/Collection;
|
||||
@@ -1481,7 +1479,7 @@ public class com/facebook/react/bridge/ReadableNativeArray : com/facebook/react/
|
||||
public fun getDouble (I)D
|
||||
public fun getDynamic (I)Lcom/facebook/react/bridge/Dynamic;
|
||||
public fun getInt (I)I
|
||||
public static fun getJNIPassCounter ()I
|
||||
public static final fun getJNIPassCounter ()I
|
||||
public fun getLong (I)J
|
||||
public synthetic fun getMap (I)Lcom/facebook/react/bridge/ReadableMap;
|
||||
public fun getMap (I)Lcom/facebook/react/bridge/ReadableNativeMap;
|
||||
@@ -1621,7 +1619,7 @@ public abstract interface class com/facebook/react/bridge/WritableMap : com/face
|
||||
public abstract fun putString (Ljava/lang/String;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public class com/facebook/react/bridge/WritableNativeArray : com/facebook/react/bridge/ReadableNativeArray, com/facebook/react/bridge/WritableArray {
|
||||
public final class com/facebook/react/bridge/WritableNativeArray : com/facebook/react/bridge/ReadableNativeArray, com/facebook/react/bridge/WritableArray {
|
||||
public fun <init> ()V
|
||||
public fun pushArray (Lcom/facebook/react/bridge/ReadableArray;)V
|
||||
public fun pushBoolean (Z)V
|
||||
@@ -2037,10 +2035,10 @@ public class com/facebook/react/defaults/DefaultReactActivityDelegate : com/face
|
||||
public final class com/facebook/react/defaults/DefaultReactHost {
|
||||
public static final field INSTANCE Lcom/facebook/react/defaults/DefaultReactHost;
|
||||
public static final fun getDefaultReactHost (Landroid/content/Context;Lcom/facebook/react/ReactNativeHost;)Lcom/facebook/react/ReactHost;
|
||||
public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lcom/facebook/react/bridge/JSBundleLoader;)Lcom/facebook/react/ReactHost;
|
||||
public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lcom/facebook/react/bridge/JSBundleLoader;Lkotlin/jvm/functions/Function1;)Lcom/facebook/react/ReactHost;
|
||||
public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lcom/facebook/react/bridge/JSBundleLoader;ILjava/lang/Object;)Lcom/facebook/react/ReactHost;
|
||||
public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lcom/facebook/react/bridge/JSBundleLoader;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/facebook/react/ReactHost;
|
||||
public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;)Lcom/facebook/react/ReactHost;
|
||||
public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;)Lcom/facebook/react/ReactHost;
|
||||
public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;ILjava/lang/Object;)Lcom/facebook/react/ReactHost;
|
||||
public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;ILjava/lang/Object;)Lcom/facebook/react/ReactHost;
|
||||
}
|
||||
|
||||
public abstract class com/facebook/react/defaults/DefaultReactNativeHost : com/facebook/react/ReactNativeHost {
|
||||
@@ -2061,6 +2059,7 @@ public final class com/facebook/react/defaults/DefaultTurboModuleManagerDelegate
|
||||
public fun <init> ()V
|
||||
public final fun addCxxReactPackage (Lkotlin/jvm/functions/Function0;)Lcom/facebook/react/defaults/DefaultTurboModuleManagerDelegate$Builder;
|
||||
public final fun addCxxReactPackage (Lkotlin/jvm/functions/Function1;)Lcom/facebook/react/defaults/DefaultTurboModuleManagerDelegate$Builder;
|
||||
public final fun addCxxReactPackages (Lkotlin/jvm/functions/Function1;)Lcom/facebook/react/defaults/DefaultTurboModuleManagerDelegate$Builder;
|
||||
public synthetic fun build (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/util/List;)Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate;
|
||||
}
|
||||
|
||||
@@ -2220,6 +2219,7 @@ public abstract interface class com/facebook/react/devsupport/HMRClient : com/fa
|
||||
public final class com/facebook/react/devsupport/InspectorFlags {
|
||||
public static final field INSTANCE Lcom/facebook/react/devsupport/InspectorFlags;
|
||||
public static final fun getFuseboxEnabled ()Z
|
||||
public static final fun getIsProfilingBuild ()Z
|
||||
}
|
||||
|
||||
public class com/facebook/react/devsupport/InspectorPackagerConnection : com/facebook/react/devsupport/IInspectorPackagerConnection {
|
||||
@@ -2761,6 +2761,7 @@ public class com/facebook/react/fabric/mounting/MountingManager {
|
||||
public fun attachRootView (ILandroid/view/View;Lcom/facebook/react/uimanager/ThemedReactContext;)V
|
||||
public fun clearJSResponder ()V
|
||||
public fun enqueuePendingEvent (IILjava/lang/String;ZLcom/facebook/react/bridge/WritableMap;I)V
|
||||
public fun experimental_prefetchResource (Lcom/facebook/react/bridge/ReactContext;Ljava/lang/String;IILcom/facebook/react/common/mapbuffer/MapBuffer;)V
|
||||
public fun getEventEmitter (II)Lcom/facebook/react/fabric/events/EventEmitterWrapper;
|
||||
public fun getSurfaceManager (I)Lcom/facebook/react/fabric/mounting/SurfaceMountingManager;
|
||||
public fun getSurfaceManagerEnforced (ILjava/lang/String;)Lcom/facebook/react/fabric/mounting/SurfaceMountingManager;
|
||||
@@ -2960,11 +2961,9 @@ public abstract interface annotation class com/facebook/react/module/annotations
|
||||
public final class com/facebook/react/module/model/ReactModuleInfo {
|
||||
public static final field Companion Lcom/facebook/react/module/model/ReactModuleInfo$Companion;
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZ)V
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZZ)V
|
||||
public final fun canOverrideExistingModule ()Z
|
||||
public static final fun classIsTurboModule (Ljava/lang/Class;)Z
|
||||
public final fun className ()Ljava/lang/String;
|
||||
public final fun hasConstants ()Z
|
||||
public final fun isCxxModule ()Z
|
||||
public final fun isTurboModule ()Z
|
||||
public final fun name ()Ljava/lang/String;
|
||||
@@ -3594,24 +3593,23 @@ public final class com/facebook/react/modules/statusbar/StatusBarModule : com/fa
|
||||
public final class com/facebook/react/modules/statusbar/StatusBarModule$Companion {
|
||||
}
|
||||
|
||||
public class com/facebook/react/modules/systeminfo/AndroidInfoHelpers {
|
||||
public final class com/facebook/react/modules/systeminfo/AndroidInfoHelpers {
|
||||
public static final field DEVICE_LOCALHOST Ljava/lang/String;
|
||||
public static final field EMULATOR_LOCALHOST Ljava/lang/String;
|
||||
public static final field GENYMOTION_LOCALHOST Ljava/lang/String;
|
||||
public static final field INSTANCE Lcom/facebook/react/modules/systeminfo/AndroidInfoHelpers;
|
||||
public static final field METRO_HOST_PROP_NAME Ljava/lang/String;
|
||||
public fun <init> ()V
|
||||
public static fun getAdbReverseTcpCommand (Landroid/content/Context;)Ljava/lang/String;
|
||||
public static fun getAdbReverseTcpCommand (Ljava/lang/Integer;)Ljava/lang/String;
|
||||
public static fun getFriendlyDeviceName ()Ljava/lang/String;
|
||||
public static fun getInspectorHostMetadata (Landroid/content/Context;)Ljava/util/Map;
|
||||
public static fun getServerHost (Landroid/content/Context;)Ljava/lang/String;
|
||||
public static fun getServerHost (Ljava/lang/Integer;)Ljava/lang/String;
|
||||
public static final fun getAdbReverseTcpCommand (I)Ljava/lang/String;
|
||||
public static final fun getAdbReverseTcpCommand (Landroid/content/Context;)Ljava/lang/String;
|
||||
public static final fun getFriendlyDeviceName ()Ljava/lang/String;
|
||||
public static final fun getInspectorHostMetadata (Landroid/content/Context;)Ljava/util/Map;
|
||||
public static final fun getServerHost (I)Ljava/lang/String;
|
||||
public static final fun getServerHost (Landroid/content/Context;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public class com/facebook/react/modules/systeminfo/AndroidInfoModule : com/facebook/fbreact/specs/NativePlatformConstantsAndroidSpec, com/facebook/react/turbomodule/core/interfaces/TurboModule {
|
||||
public final class com/facebook/react/modules/systeminfo/AndroidInfoModule : com/facebook/fbreact/specs/NativePlatformConstantsAndroidSpec {
|
||||
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;)V
|
||||
public fun getAndroidID ()Ljava/lang/String;
|
||||
public fun getTypedExportedConstants ()Ljava/util/Map;
|
||||
public fun invalidate ()V
|
||||
}
|
||||
|
||||
@@ -5285,6 +5283,8 @@ public abstract class com/facebook/react/uimanager/ViewManager : com/facebook/re
|
||||
public fun createView (ILcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;Lcom/facebook/react/touch/JSResponderHandler;)Landroid/view/View;
|
||||
protected fun createViewInstance (ILcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Landroid/view/View;
|
||||
protected abstract fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View;
|
||||
protected fun experimental_isPrefetchingEnabled ()Z
|
||||
public fun experimental_prefetchResource (Lcom/facebook/react/bridge/ReactContext;IILcom/facebook/react/common/mapbuffer/MapBuffer;)V
|
||||
public fun getCommandsMap ()Ljava/util/Map;
|
||||
protected fun getDelegate ()Lcom/facebook/react/uimanager/ViewManagerDelegate;
|
||||
public fun getExportedCustomBubblingEventTypeConstants ()Ljava/util/Map;
|
||||
@@ -6022,11 +6022,6 @@ public final class com/facebook/react/uimanager/style/CornerRadii {
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/uimanager/style/Gradient {
|
||||
public fun <init> (Lcom/facebook/react/bridge/ReadableMap;Landroid/content/Context;)V
|
||||
public final fun getShader (Landroid/graphics/Rect;)Landroid/graphics/Shader;
|
||||
}
|
||||
|
||||
public abstract class com/facebook/react/uimanager/style/LogicalEdge : java/lang/Enum {
|
||||
public static final field ALL Lcom/facebook/react/uimanager/style/LogicalEdge;
|
||||
public static final field BLOCK Lcom/facebook/react/uimanager/style/LogicalEdge;
|
||||
@@ -7027,11 +7022,10 @@ public abstract interface class com/facebook/react/views/scroll/ReactScrollViewH
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/scroll/ReactScrollViewHelper$ReactScrollViewScrollState {
|
||||
public fun <init> (I)V
|
||||
public fun <init> ()V
|
||||
public final fun getDecelerationRate ()F
|
||||
public final fun getFinalAnimatedPositionScroll ()Landroid/graphics/Point;
|
||||
public final fun getLastStateUpdateScroll ()Landroid/graphics/Point;
|
||||
public final fun getLayoutDirection ()I
|
||||
public final fun getScrollAwayPaddingTop ()I
|
||||
public final fun isCanceled ()Z
|
||||
public final fun isFinished ()Z
|
||||
@@ -7835,8 +7829,6 @@ public class com/facebook/react/views/view/ReactDrawableHelper {
|
||||
|
||||
public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGroup, com/facebook/react/touch/ReactHitSlopView, com/facebook/react/touch/ReactInterceptingViewGroup, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/uimanager/ReactPointerEventsView, com/facebook/react/uimanager/ReactZIndexedViewGroup {
|
||||
public fun <init> (Landroid/content/Context;)V
|
||||
public fun addView (Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V
|
||||
protected fun addViewInLayout (Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;Z)Z
|
||||
protected fun dispatchDraw (Landroid/graphics/Canvas;)V
|
||||
public fun dispatchGenericMotionEvent (Landroid/view/MotionEvent;)Z
|
||||
public fun dispatchProvideStructure (Landroid/view/ViewStructure;)V
|
||||
@@ -7844,7 +7836,6 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
|
||||
public fun draw (Landroid/graphics/Canvas;)V
|
||||
protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z
|
||||
protected fun getChildDrawingOrder (II)I
|
||||
public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z
|
||||
public fun getClippingRect (Landroid/graphics/Rect;)V
|
||||
public fun getHitSlopRect ()Landroid/graphics/Rect;
|
||||
public fun getOverflow ()Ljava/lang/String;
|
||||
@@ -7860,11 +7851,8 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
|
||||
protected fun onMeasure (II)V
|
||||
protected fun onSizeChanged (IIII)V
|
||||
public fun onTouchEvent (Landroid/view/MotionEvent;)Z
|
||||
public fun removeView (Landroid/view/View;)V
|
||||
public fun removeViewAt (I)V
|
||||
public fun removeViewInLayout (Landroid/view/View;)V
|
||||
public fun removeViews (II)V
|
||||
public fun removeViewsInLayout (II)V
|
||||
public fun onViewAdded (Landroid/view/View;)V
|
||||
public fun onViewRemoved (Landroid/view/View;)V
|
||||
public fun requestLayout ()V
|
||||
public fun setBackfaceVisibility (Ljava/lang/String;)V
|
||||
public fun setBackfaceVisibilityDependantOpacity ()V
|
||||
|
||||
@@ -31,10 +31,8 @@ if(CCACHE_FOUND)
|
||||
endif(CCACHE_FOUND)
|
||||
|
||||
set(BUILD_DIR ${PROJECT_BUILD_DIR})
|
||||
if(CMAKE_HOST_WIN32)
|
||||
string(REPLACE "\\" "/" BUILD_DIR ${BUILD_DIR})
|
||||
string(REPLACE "\\" "/" REACT_ANDROID_DIR ${REACT_ANDROID_DIR})
|
||||
endif()
|
||||
file(TO_CMAKE_PATH "${BUILD_DIR}" BUILD_DIR)
|
||||
file(TO_CMAKE_PATH "${REACT_ANDROID_DIR}" REACT_ANDROID_DIR)
|
||||
|
||||
if (PROJECT_ROOT_DIR)
|
||||
# This empty `if` is just to silence a CMake warning and make sure the `PROJECT_ROOT_DIR`
|
||||
@@ -45,11 +43,6 @@ file(GLOB input_SRC CONFIGURE_DEPENDS
|
||||
${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
|
||||
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
|
||||
|
||||
# Ensure that `input_SRC` paths use forward slashes
|
||||
foreach(path IN LISTS input_SRC)
|
||||
string(REPLACE "\\" "/" path "${path}")
|
||||
endforeach()
|
||||
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
|
||||
|
||||
target_include_directories(${CMAKE_PROJECT_NAME}
|
||||
|
||||
@@ -29,7 +29,12 @@
|
||||
|
||||
#include <DefaultComponentsRegistry.h>
|
||||
#include <DefaultTurboModuleManagerDelegate.h>
|
||||
#if __has_include("<autolinking.h>")
|
||||
#define AUTOLINKING_AVAILABLE 1
|
||||
#include <autolinking.h>
|
||||
#else
|
||||
#define AUTOLINKING_AVAILABLE 0
|
||||
#endif
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
#include <rncore.h>
|
||||
@@ -56,8 +61,10 @@ void registerComponents(
|
||||
REACT_NATIVE_APP_COMPONENT_REGISTRATION(registry);
|
||||
#endif
|
||||
|
||||
#if AUTOLINKING_AVAILABLE
|
||||
// And we fallback to the components autolinked
|
||||
autolinking_registerProviders(registry);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule> cxxModuleProvider(
|
||||
@@ -71,8 +78,12 @@ std::shared_ptr<TurboModule> cxxModuleProvider(
|
||||
// return std::make_shared<NativeCxxModuleExample>(jsInvoker);
|
||||
// }
|
||||
|
||||
#if AUTOLINKING_AVAILABLE
|
||||
// And we fallback to the CXX module providers autolinked
|
||||
return autolinking_cxxModuleProvider(name, jsInvoker);
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule> javaModuleProvider(
|
||||
@@ -101,10 +112,12 @@ std::shared_ptr<TurboModule> javaModuleProvider(
|
||||
return module;
|
||||
}
|
||||
|
||||
#if AUTOLINKING_AVAILABLE
|
||||
// And we fallback to the module providers autolinked
|
||||
if (auto module = autolinking_ModuleProvider(name, params)) {
|
||||
return module;
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,13 @@ fun getSDKPath(): String {
|
||||
fun getSDKManagerPath(): String {
|
||||
val metaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager")
|
||||
val ossSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager")
|
||||
val windowsMetaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager.bat")
|
||||
val windowsOssSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager.bat")
|
||||
return when {
|
||||
metaSdkManagerPath.exists() -> metaSdkManagerPath.absolutePath
|
||||
windowsMetaSdkManagerPath.exists() -> windowsMetaSdkManagerPath.absolutePath
|
||||
ossSdkManagerPath.exists() -> ossSdkManagerPath.absolutePath
|
||||
windowsOssSdkManagerPath.exists() -> windowsOssSdkManagerPath.absolutePath
|
||||
else -> throw GradleException("Could not find sdkmanager executable.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
as the min SDK to lint the file.-->
|
||||
<uses-sdk
|
||||
android:minSdkVersion="24"
|
||||
android:targetSdkVersion="34"
|
||||
android:targetSdkVersion="35"
|
||||
/>
|
||||
|
||||
</manifest>
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.proguard.annotations;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.CLASS;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Add this annotation to a class, to keep all "void set*(***)" and get* methods.
|
||||
*
|
||||
* <p>This is useful for classes that are controlled by animator-like classes that control various
|
||||
* properties with reflection.
|
||||
*
|
||||
* <p><b>NOTE:</b> This is <em>not</em> needed for Views because their getters and setters are
|
||||
* automatically kept by the default Android SDK ProGuard config.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(CLASS)
|
||||
public @interface KeepGettersAndSetters {}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.proguard.annotations
|
||||
|
||||
import kotlin.annotation.AnnotationRetention
|
||||
import kotlin.annotation.AnnotationTarget
|
||||
|
||||
/**
|
||||
* Add this annotation to a class, to keep all "void set*(***)" and get* methods.
|
||||
*
|
||||
* This is useful for classes that are controlled by animator-like classes that control various
|
||||
* properties with reflection.
|
||||
*
|
||||
* **NOTE:** This is *not* needed for Views because their getters and setters are automatically kept
|
||||
* by the default Android SDK ProGuard config.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
public annotation class KeepGettersAndSetters
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ModuleHolder;
|
||||
import com.facebook.react.bridge.ModuleSpec;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
|
||||
import com.facebook.react.module.model.ReactModuleInfo;
|
||||
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/** Abstract class that supports lazy loading of NativeModules by default. */
|
||||
public abstract class BaseReactPackage implements ReactPackage {
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
||||
throw new UnsupportedOperationException(
|
||||
"createNativeModules method is not supported. Use getModule() method instead.");
|
||||
}
|
||||
|
||||
/**
|
||||
* The API needed for TurboModules. Given a module name, it returns an instance of {@link
|
||||
* NativeModule} for the name
|
||||
*
|
||||
* @param name name of the Native Module
|
||||
* @param reactContext {@link ReactApplicationContext} context for this
|
||||
*/
|
||||
@Override
|
||||
public abstract @Nullable NativeModule getModule(
|
||||
@NonNull String name, @NonNull ReactApplicationContext reactContext);
|
||||
|
||||
/**
|
||||
* This is a temporary method till we implement TurboModules. Once we implement TurboModules, we
|
||||
* will be able to directly call {@link BaseReactPackage#getModule(String,
|
||||
* ReactApplicationContext)} This method will be removed when TurboModule implementation is
|
||||
* complete
|
||||
*
|
||||
* @param reactContext
|
||||
* @return
|
||||
*/
|
||||
/** package */
|
||||
Iterable<ModuleHolder> getNativeModuleIterator(final ReactApplicationContext reactContext) {
|
||||
final Set<Map.Entry<String, ReactModuleInfo>> entrySet =
|
||||
getReactModuleInfoProvider().getReactModuleInfos().entrySet();
|
||||
final Iterator<Map.Entry<String, ReactModuleInfo>> entrySetIterator = entrySet.iterator();
|
||||
// This should ideally be an IteratorConvertor, but we don't have any internal library for it
|
||||
return () ->
|
||||
new Iterator<ModuleHolder>() {
|
||||
@Nullable Map.Entry<String, ReactModuleInfo> nextEntry = null;
|
||||
|
||||
private void findNext() {
|
||||
while (entrySetIterator.hasNext()) {
|
||||
Map.Entry<String, ReactModuleInfo> entry = entrySetIterator.next();
|
||||
ReactModuleInfo reactModuleInfo = entry.getValue();
|
||||
|
||||
// This Iterator is used to create the NativeModule registry. The NativeModule
|
||||
// registry must not have TurboModules. Therefore, if TurboModules are enabled, and
|
||||
// the current NativeModule is a TurboModule, we need to skip iterating over it.
|
||||
if (ReactNativeFeatureFlags.useTurboModules() && reactModuleInfo.isTurboModule()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextEntry = entry;
|
||||
return;
|
||||
}
|
||||
nextEntry = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (nextEntry == null) {
|
||||
findNext();
|
||||
}
|
||||
return nextEntry != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleHolder next() {
|
||||
if (nextEntry == null) {
|
||||
findNext();
|
||||
}
|
||||
|
||||
if (nextEntry == null) {
|
||||
throw new NoSuchElementException("ModuleHolder not found");
|
||||
}
|
||||
|
||||
Map.Entry<String, ReactModuleInfo> entry = nextEntry;
|
||||
|
||||
// Advance iterator
|
||||
findNext();
|
||||
String name = entry.getKey();
|
||||
ReactModuleInfo reactModuleInfo = entry.getValue();
|
||||
return new ModuleHolder(reactModuleInfo, new ModuleHolderProvider(name, reactContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Cannot remove native modules from the list");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext react application context that can be used to create View Managers.
|
||||
* @return list of module specs that can create the View Managers.
|
||||
*/
|
||||
protected List<ModuleSpec> getViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
||||
List<ModuleSpec> viewManagerModuleSpecs = getViewManagers(reactContext);
|
||||
if (viewManagerModuleSpecs == null || viewManagerModuleSpecs.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<ViewManager> viewManagers = new ArrayList<>();
|
||||
for (ModuleSpec moduleSpec : viewManagerModuleSpecs) {
|
||||
viewManagers.add((ViewManager) moduleSpec.getProvider().get());
|
||||
}
|
||||
return viewManagers;
|
||||
}
|
||||
|
||||
public abstract ReactModuleInfoProvider getReactModuleInfoProvider();
|
||||
|
||||
private class ModuleHolderProvider implements Provider<NativeModule> {
|
||||
|
||||
private final String mName;
|
||||
private final ReactApplicationContext mReactContext;
|
||||
|
||||
public ModuleHolderProvider(String name, ReactApplicationContext reactContext) {
|
||||
mName = name;
|
||||
mReactContext = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable NativeModule get() {
|
||||
return getModule(mName, mReactContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react
|
||||
|
||||
import com.facebook.react.bridge.ModuleHolder
|
||||
import com.facebook.react.bridge.ModuleSpec
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import com.facebook.react.module.model.ReactModuleInfo
|
||||
import com.facebook.react.module.model.ReactModuleInfoProvider
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
import java.util.ArrayList
|
||||
import java.util.NoSuchElementException
|
||||
import javax.inject.Provider
|
||||
|
||||
/** Abstract class that supports lazy loading of NativeModules by default. */
|
||||
public abstract class BaseReactPackage : ReactPackage {
|
||||
|
||||
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
||||
throw UnsupportedOperationException(
|
||||
"createNativeModules method is not supported. Use getModule() method instead.")
|
||||
}
|
||||
|
||||
/**
|
||||
* The API needed for TurboModules. Given a module name, it returns an instance of [NativeModule]
|
||||
* for the name
|
||||
*
|
||||
* @param name name of the Native Module
|
||||
* @param reactContext [ReactApplicationContext] context for this
|
||||
*/
|
||||
abstract override fun getModule(
|
||||
name: String,
|
||||
reactContext: ReactApplicationContext
|
||||
): NativeModule?
|
||||
|
||||
/**
|
||||
* This is a temporary method till we implement TurboModules. Once we implement TurboModules, we
|
||||
* will be able to directly call [BaseReactPackage#getModule(String, ReactApplicationContext)]
|
||||
* This method will be removed when TurboModule implementation is complete
|
||||
*
|
||||
* @param reactContext [ReactApplicationContext]
|
||||
* @return
|
||||
*/
|
||||
internal fun getNativeModuleIterator(
|
||||
reactContext: ReactApplicationContext
|
||||
): Iterable<ModuleHolder> {
|
||||
val entrySet = getReactModuleInfoProvider().getReactModuleInfos().entries
|
||||
val entrySetIterator = entrySet.iterator()
|
||||
// This should ideally be an IteratorConvertor, but we don't have any internal library for it
|
||||
return Iterable {
|
||||
object : Iterator<ModuleHolder> {
|
||||
var nextEntry: Map.Entry<String, ReactModuleInfo>? = null
|
||||
|
||||
private fun findNext() {
|
||||
while (entrySetIterator.hasNext()) {
|
||||
val entry = entrySetIterator.next()
|
||||
val reactModuleInfo = entry.value
|
||||
|
||||
// This Iterator is used to create the NativeModule registry. The NativeModule
|
||||
// registry must not have TurboModules. Therefore, if TurboModules are enabled, and
|
||||
// the current NativeModule is a TurboModule, we need to skip iterating over it.
|
||||
if (ReactNativeFeatureFlags.useTurboModules() && reactModuleInfo.isTurboModule) {
|
||||
continue
|
||||
}
|
||||
|
||||
nextEntry = entry
|
||||
return
|
||||
}
|
||||
nextEntry = null
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
if (nextEntry == null) {
|
||||
findNext()
|
||||
}
|
||||
return nextEntry != null
|
||||
}
|
||||
|
||||
override fun next(): ModuleHolder {
|
||||
if (nextEntry == null) {
|
||||
findNext()
|
||||
}
|
||||
|
||||
val entry = nextEntry ?: throw NoSuchElementException("ModuleHolder not found")
|
||||
|
||||
// Advance iterator
|
||||
findNext()
|
||||
|
||||
return ModuleHolder(entry.value, ModuleHolderProvider(entry.key, reactContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext react application context that can be used to create View Managers.
|
||||
* @return list of module specs that can create the View Managers.
|
||||
*/
|
||||
protected open fun getViewManagers(reactContext: ReactApplicationContext): List<ModuleSpec> =
|
||||
emptyList()
|
||||
|
||||
override fun createViewManagers(
|
||||
reactContext: ReactApplicationContext
|
||||
): List<ViewManager<in Nothing, in Nothing>> {
|
||||
val viewManagerModuleSpecs = getViewManagers(reactContext)
|
||||
if (viewManagerModuleSpecs.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val viewManagers: MutableList<ViewManager<*, *>> = ArrayList()
|
||||
for (moduleSpec in viewManagerModuleSpecs) {
|
||||
viewManagers.add((moduleSpec.provider.get() as ViewManager<*, *>))
|
||||
}
|
||||
return viewManagers
|
||||
}
|
||||
|
||||
public abstract fun getReactModuleInfoProvider(): ReactModuleInfoProvider
|
||||
|
||||
private inner class ModuleHolderProvider(
|
||||
private val name: String,
|
||||
private val reactContext: ReactApplicationContext
|
||||
) : Provider<NativeModule?> {
|
||||
override fun get(): NativeModule? = getModule(name, reactContext)
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/** Provider for an already initialized and non-lazy NativeModule. */
|
||||
class EagerModuleProvider implements Provider<NativeModule> {
|
||||
|
||||
private final NativeModule mModule;
|
||||
|
||||
public EagerModuleProvider(NativeModule module) {
|
||||
mModule = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeModule get() {
|
||||
return mModule;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,8 @@ public interface ReactHost {
|
||||
* @param reason describing why ReactHost is being destroyed (e.g. memory pressure)
|
||||
* @param ex exception that caused the trigger to destroy ReactHost (or null) This exception will
|
||||
* be used to log properly the cause of destroy operation.
|
||||
* @param onDestroyFinished callback that will be called when React Native gets destroyed.
|
||||
* @param onDestroyFinished callback that will be called when React Native gets destroyed, the
|
||||
* callback will run on a background thread.
|
||||
* @return A task that completes when React Native gets destroyed.
|
||||
*/
|
||||
public fun destroy(
|
||||
|
||||
+2
-2
@@ -8,7 +8,6 @@
|
||||
package com.facebook.react;
|
||||
|
||||
import static com.facebook.react.ReactInstanceManager.initializeSoLoaderIfNecessary;
|
||||
import static com.facebook.react.modules.systeminfo.AndroidInfoHelpers.getFriendlyDeviceName;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
@@ -37,6 +36,7 @@ import com.facebook.react.internal.ChoreographerProvider;
|
||||
import com.facebook.react.jscexecutor.JSCExecutor;
|
||||
import com.facebook.react.jscexecutor.JSCExecutorFactory;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers;
|
||||
import com.facebook.react.packagerconnection.RequestHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -339,7 +339,7 @@ public class ReactInstanceManagerBuilder {
|
||||
// We use the name of the device and the app for debugging & metrics
|
||||
//noinspection ConstantConditions
|
||||
String appName = mApplication.getPackageName();
|
||||
String deviceName = getFriendlyDeviceName();
|
||||
String deviceName = AndroidInfoHelpers.getFriendlyDeviceName();
|
||||
|
||||
return new ReactInstanceManager(
|
||||
mApplication,
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.common.annotations.DeprecatedInNewArchitecture;
|
||||
import com.facebook.react.common.annotations.UnstableReactNativeAPI;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Main interface for providing additional capabilities to the catalyst framework by couple of
|
||||
* different means:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Registering new native modules
|
||||
* <li>Registering new JS modules that may be accessed from native modules or from other parts of
|
||||
* the native code (requiring JS modules from the package doesn't mean it will automatically
|
||||
* be included as a part of the JS bundle, so there should be a corresponding piece of code on
|
||||
* JS side that will require implementation of that JS module so that it gets bundled)
|
||||
* <li>Registering custom native views (view managers) and custom event types
|
||||
* <li>Registering natively packaged assets/resources (e.g. images) exposed to JS
|
||||
* </ol>
|
||||
*
|
||||
* <p>TODO(6788500, 6788507): Implement support for adding custom views, events and resources
|
||||
*/
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public interface ReactPackage {
|
||||
|
||||
/**
|
||||
* @param reactContext react application context that can be used to create modules
|
||||
* @return list of native modules to register with the newly created catalyst instance This method
|
||||
* is deprecated in the new Architecture of React Native.
|
||||
*/
|
||||
@NonNull
|
||||
@DeprecatedInNewArchitecture
|
||||
List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext);
|
||||
|
||||
/**
|
||||
* @return a list of view managers that should be registered with {@link UIManagerModule}
|
||||
*/
|
||||
@NonNull
|
||||
List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext);
|
||||
|
||||
/**
|
||||
* Given a module name, it returns an instance of {@link NativeModule} for the name
|
||||
*
|
||||
* @param name name of the Native Module
|
||||
* @param reactContext {@link ReactApplicationContext} context for this
|
||||
*/
|
||||
@Nullable
|
||||
@UnstableReactNativeAPI
|
||||
default NativeModule getModule(
|
||||
@NonNull String name, @NonNull ReactApplicationContext reactContext) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react
|
||||
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.common.annotations.DeprecatedInNewArchitecture
|
||||
import com.facebook.react.common.annotations.StableReactNativeAPI
|
||||
import com.facebook.react.uimanager.UIManagerModule
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
|
||||
/**
|
||||
* Main interface for providing additional capabilities to the catalyst framework by couple of
|
||||
* different means:
|
||||
* 1. Registering new native modules
|
||||
* 1. Registering new JS modules that may be accessed from native modules or from other parts of the
|
||||
* native code (requiring JS modules from the package doesn't mean it will automatically be
|
||||
* included as a part of the JS bundle, so there should be a corresponding piece of code on JS
|
||||
* side that will require implementation of that JS module so that it gets bundled)
|
||||
* 1. Registering custom native views (view managers) and custom event types
|
||||
* 1. Registering natively packaged assets/resources (e.g. images) exposed to JS
|
||||
*
|
||||
* TODO(6788500, 6788507): Implement support for adding custom views, events and resources
|
||||
*/
|
||||
public interface ReactPackage {
|
||||
|
||||
/**
|
||||
* @param reactContext react application context that can be used to create modules
|
||||
* @return list of native modules to register with the newly created catalyst instance This method
|
||||
* is deprecated in the new Architecture of React Native.
|
||||
*/
|
||||
@DeprecatedInNewArchitecture(message = "Migrate to BaseReactPackage and implement getModule")
|
||||
public fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule>
|
||||
|
||||
/** @return a list of view managers that should be registered with [UIManagerModule] */
|
||||
public fun createViewManagers(
|
||||
reactContext: ReactApplicationContext
|
||||
): List<ViewManager<in Nothing, in Nothing>>
|
||||
|
||||
/**
|
||||
* Given a module name, it returns an instance of [NativeModule] for the name
|
||||
*
|
||||
* @param name name of the Native Module
|
||||
* @param reactContext [ReactApplicationContext] context for this
|
||||
*/
|
||||
@StableReactNativeAPI
|
||||
public fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = null
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.ModuleHolder;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
class ReactPackageHelper {
|
||||
/**
|
||||
* A helper method to iterate over a list of Native Modules and convert them to an iterable.
|
||||
*
|
||||
* @param reactPackage
|
||||
* @param reactApplicationContext
|
||||
* @param reactInstanceManager
|
||||
* @return
|
||||
*/
|
||||
/** package */
|
||||
static Iterable<ModuleHolder> getNativeModuleIterator(
|
||||
ReactPackage reactPackage, ReactApplicationContext reactApplicationContext) {
|
||||
FLog.d(
|
||||
ReactConstants.TAG,
|
||||
reactPackage.getClass().getSimpleName()
|
||||
+ " is not a LazyReactPackage, falling back to old version.");
|
||||
final List<NativeModule> nativeModules =
|
||||
reactPackage.createNativeModules(reactApplicationContext);
|
||||
return () ->
|
||||
new Iterator<ModuleHolder>() {
|
||||
int position = 0;
|
||||
|
||||
@Override
|
||||
public ModuleHolder next() {
|
||||
return new ModuleHolder(nativeModules.get(position++));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return position < nativeModules.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Cannot remove methods ");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react
|
||||
|
||||
import com.facebook.common.logging.FLog
|
||||
import com.facebook.react.bridge.ModuleHolder
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.common.ReactConstants
|
||||
|
||||
internal object ReactPackageHelper {
|
||||
|
||||
/**
|
||||
* A helper method to iterate over a list of Native Modules and convert them to an iterable.
|
||||
*
|
||||
* @param reactPackage
|
||||
* @param reactApplicationContext
|
||||
* @param reactInstanceManager
|
||||
* @return
|
||||
*/
|
||||
fun getNativeModuleIterator(
|
||||
reactPackage: ReactPackage,
|
||||
reactApplicationContext: ReactApplicationContext
|
||||
): Iterable<ModuleHolder> {
|
||||
FLog.d(
|
||||
ReactConstants.TAG,
|
||||
"${reactPackage.javaClass.simpleName} is not a LazyReactPackage, falling back to old version.")
|
||||
val nativeModules = reactPackage.createNativeModules(reactApplicationContext)
|
||||
return Iterable {
|
||||
object : Iterator<ModuleHolder> {
|
||||
var position = 0
|
||||
|
||||
override fun next(): ModuleHolder = ModuleHolder(nativeModules[position++])
|
||||
|
||||
override fun hasNext(): Boolean = position < nativeModules.size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -8,13 +8,16 @@
|
||||
package com.facebook.react;
|
||||
|
||||
import static com.facebook.infer.annotation.ThreadConfined.UI;
|
||||
import static com.facebook.react.uimanager.BlendModeHelper.needsIsolatedLayer;
|
||||
import static com.facebook.react.uimanager.common.UIManagerType.DEFAULT;
|
||||
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
|
||||
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.BlendMode;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
@@ -65,6 +68,7 @@ import com.facebook.react.uimanager.RootView;
|
||||
import com.facebook.react.uimanager.RootViewUtil;
|
||||
import com.facebook.react.uimanager.UIManagerHelper;
|
||||
import com.facebook.react.uimanager.common.UIManagerType;
|
||||
import com.facebook.react.uimanager.common.ViewUtil;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.systrace.Systrace;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -290,6 +294,30 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
|
||||
BlendMode mixBlendMode = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
&& ViewUtil.getUIManagerType(this) == UIManagerType.FABRIC
|
||||
&& needsIsolatedLayer(this)) {
|
||||
mixBlendMode = (BlendMode) child.getTag(R.id.mix_blend_mode);
|
||||
if (mixBlendMode != null) {
|
||||
Paint p = new Paint();
|
||||
p.setBlendMode(mixBlendMode);
|
||||
canvas.saveLayer(0, 0, getWidth(), getHeight(), p);
|
||||
}
|
||||
}
|
||||
|
||||
boolean result = super.drawChild(canvas, child, drawingTime);
|
||||
|
||||
if (mixBlendMode != null) {
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent ev) {
|
||||
if (!hasActiveReactContext() || !isViewAttachedToReactInstance()) {
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
|
||||
import com.facebook.react.common.annotations.DeprecatedInNewArchitecture;
|
||||
|
||||
/** This will eventually replace {@link LazyReactPackage} when TurboModules are finally done. */
|
||||
@DeprecatedInNewArchitecture(message = "Use BaseReactPackage instead")
|
||||
public abstract class TurboReactPackage extends BaseReactPackage {}
|
||||
+8
-8
@@ -5,24 +5,24 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react;
|
||||
package com.facebook.react
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import java.util.Collection;
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
|
||||
public interface ViewManagerOnDemandReactPackage {
|
||||
/**
|
||||
* Provides a list of names of ViewManagers with which these modules can be accessed from JS.
|
||||
* Typically, this is ViewManager.getName().
|
||||
*/
|
||||
Collection<String> getViewManagerNames(ReactApplicationContext reactContext);
|
||||
public fun getViewManagerNames(reactContext: ReactApplicationContext): Collection<String>
|
||||
|
||||
/**
|
||||
* Creates and returns a ViewManager with a specific name {@param viewManagerName}. It's up to an
|
||||
* implementing package how to interpret the name.
|
||||
*/
|
||||
@Nullable
|
||||
ViewManager createViewManager(ReactApplicationContext reactContext, String viewManagerName);
|
||||
public fun createViewManager(
|
||||
reactContext: ReactApplicationContext,
|
||||
viewManagerName: String
|
||||
): ViewManager<in Nothing, in Nothing>?
|
||||
}
|
||||
+2
-2
@@ -130,7 +130,7 @@ public class InterpolationAnimatedNode(config: ReadableMap) : ValueAnimatedNode(
|
||||
val outputRange = arrayOfNulls<DoubleArray>(size)
|
||||
|
||||
// Match the first pattern into a List, since we don't know its length yet
|
||||
var m = numericPattern.matcher(array.getString(0))
|
||||
var m = numericPattern.matcher(array.getString(0) ?: "")
|
||||
val firstOutputRange: MutableList<Double> = ArrayList()
|
||||
while (m.find()) {
|
||||
firstOutputRange.add(m.group().toDouble())
|
||||
@@ -143,7 +143,7 @@ public class InterpolationAnimatedNode(config: ReadableMap) : ValueAnimatedNode(
|
||||
for (i in 1 until size) {
|
||||
val outputArr = DoubleArray(firstOutputRangeArr.size)
|
||||
var j = 0
|
||||
m = numericPattern.matcher(array.getString(i))
|
||||
m = numericPattern.matcher(array.getString(i) ?: "")
|
||||
while (m.find() && j < firstOutputRangeArr.size) {
|
||||
outputArr[j++] = m.group().toDouble()
|
||||
}
|
||||
|
||||
+3
-1
@@ -45,7 +45,9 @@ internal class ObjectAnimatedNode(
|
||||
ReadableType.String -> result.pushString(source.getString(i))
|
||||
ReadableType.Map -> {
|
||||
val map = source.getMap(i)
|
||||
if (map.hasKey(NODE_TAG_KEY) && map.getType(NODE_TAG_KEY) == ReadableType.Number) {
|
||||
if (map != null &&
|
||||
map.hasKey(NODE_TAG_KEY) &&
|
||||
map.getType(NODE_TAG_KEY) == ReadableType.Number) {
|
||||
val node = nativeAnimatedNodesManager.getNodeById(map.getInt(NODE_TAG_KEY))
|
||||
requireNotNull(node) { "Mapped value node does not exist" }
|
||||
if (node is ValueAnimatedNode) {
|
||||
|
||||
+5
-5
@@ -19,15 +19,15 @@ internal class TransformAnimatedNode(
|
||||
config: ReadableMap,
|
||||
private val nativeAnimatedNodesManager: NativeAnimatedNodesManager
|
||||
) : AnimatedNode() {
|
||||
private val transformConfigs: MutableList<TransformConfig>
|
||||
private val transformConfigs: List<TransformConfig>
|
||||
|
||||
init {
|
||||
val transforms = config.getArray("transforms")
|
||||
transformConfigs =
|
||||
if (transforms == null) mutableListOf()
|
||||
if (transforms == null) emptyList()
|
||||
else
|
||||
MutableList<TransformConfig>(transforms.size()) { i ->
|
||||
val transformConfigMap = transforms.getMap(i)
|
||||
List<TransformConfig>(transforms.size()) { i ->
|
||||
val transformConfigMap = checkNotNull(transforms.getMap(i))
|
||||
val property = transformConfigMap.getString("property")
|
||||
val type = transformConfigMap.getString("type")
|
||||
if (type == "animated") {
|
||||
@@ -46,7 +46,7 @@ internal class TransformAnimatedNode(
|
||||
|
||||
public fun collectViewUpdates(propsMap: JavaOnlyMap) {
|
||||
val transforms =
|
||||
MutableList<JavaOnlyMap>(transformConfigs.size) { i ->
|
||||
List<JavaOnlyMap>(transformConfigs.size) { i ->
|
||||
val transformConfig = transformConfigs[i]
|
||||
val transform =
|
||||
if (transformConfig is AnimatedTransformConfig) {
|
||||
|
||||
+4
-4
@@ -34,7 +34,7 @@ public class ColorPropConverter {
|
||||
private static final String ATTR_SEGMENT = "attr/";
|
||||
|
||||
@Nullable
|
||||
private static Integer getColorInteger(Object value, Context context) {
|
||||
private static Integer getColorInteger(@Nullable Object value, Context context) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public class ColorPropConverter {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Color getColorInstance(Object value, Context context) {
|
||||
public static Color getColorInstance(@Nullable Object value, Context context) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ public class ColorPropConverter {
|
||||
"ColorValue: the value must be a number or Object.");
|
||||
}
|
||||
|
||||
public static Integer getColor(Object value, Context context) {
|
||||
public static Integer getColor(@Nullable Object value, Context context) {
|
||||
try {
|
||||
if (supportWideGamut()) {
|
||||
Color color = getColorInstance(value, context);
|
||||
@@ -150,7 +150,7 @@ public class ColorPropConverter {
|
||||
return getColorInteger(value, context);
|
||||
}
|
||||
|
||||
public static Integer getColor(Object value, Context context, int defaultInt) {
|
||||
public static Integer getColor(@Nullable Object value, Context context, int defaultInt) {
|
||||
try {
|
||||
return getColor(value, context);
|
||||
} catch (JSApplicationCausedNativeException e) {
|
||||
|
||||
+3
-3
@@ -73,13 +73,13 @@ public class JavaOnlyArray : ReadableArray, WritableArray {
|
||||
|
||||
override fun getLong(index: Int): Long = (backingList[index] as Number).toLong()
|
||||
|
||||
override fun getString(index: Int): String = backingList[index] as String
|
||||
override fun getString(index: Int): String? = backingList[index] as String?
|
||||
|
||||
override fun getArray(index: Int): ReadableArray = backingList[index] as ReadableArray
|
||||
override fun getArray(index: Int): ReadableArray? = backingList[index] as ReadableArray?
|
||||
|
||||
override fun getBoolean(index: Int): Boolean = backingList[index] as Boolean
|
||||
|
||||
override fun getMap(index: Int): ReadableMap = backingList[index] as ReadableMap
|
||||
override fun getMap(index: Int): ReadableMap? = backingList[index] as ReadableMap?
|
||||
|
||||
override fun getDynamic(index: Int): Dynamic = DynamicFromArray.create(this, index)
|
||||
|
||||
|
||||
+2
-3
@@ -25,7 +25,6 @@ import com.facebook.react.bridge.interop.InteropModuleRegistry;
|
||||
import com.facebook.react.bridge.queue.MessageQueueThread;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.common.annotations.DeprecatedInNewArchitecture;
|
||||
import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collection;
|
||||
@@ -503,8 +502,8 @@ public abstract class ReactContext extends ContextWrapper {
|
||||
*/
|
||||
public abstract @Nullable CallInvokerHolder getJSCallInvokerHolder();
|
||||
|
||||
@DeprecatedInNewArchitecture(
|
||||
message =
|
||||
@Deprecated(
|
||||
since =
|
||||
"This method will be deprecated later as part of Stable APIs with bridge removal and not"
|
||||
+ " encouraged usage.")
|
||||
/**
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ import java.util.ArrayList
|
||||
* to Kotlin.
|
||||
*/
|
||||
public interface ReadableArray {
|
||||
public fun getArray(index: Int): ReadableArray
|
||||
public fun getArray(index: Int): ReadableArray?
|
||||
|
||||
public fun getBoolean(index: Int): Boolean
|
||||
|
||||
@@ -26,9 +26,9 @@ public interface ReadableArray {
|
||||
|
||||
public fun getLong(index: Int): Long
|
||||
|
||||
public fun getMap(index: Int): ReadableMap
|
||||
public fun getMap(index: Int): ReadableMap?
|
||||
|
||||
public fun getString(index: Int): String
|
||||
public fun getString(index: Int): String?
|
||||
|
||||
public fun getType(index: Int): ReadableType
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user