Compare commits

...
Author SHA1 Message Date
React Native Bot 3c2cc3852d Release 0.82.0-rc.1
#publish-packages-to-npm&next
2025-09-09 10:02:01 +00:00
Nicola Corti f68b918c89 [LOCAL] Remove unnecesary quote in hermesVersionProvider 2025-09-08 15:51:35 +01:00
Nicola Corti ee08261123 [LOCAL] Temporarily disable prebuilds for 0.82 branch 2025-09-08 15:40:05 +01:00
Rubén Norte ac2fe26cf2 Define Flow types for Performance APIs (#53433)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53433

Changelog: [internal]

This adds the definitions for the Web Performance APIs in the global scope.

Reviewed By: zeyap

Differential Revision: D80811659

fbshipit-source-id: a81117a27a480ba03f8feb2e813a3a66a10307f9
2025-09-08 14:56:46 +01:00
Rubén Norte cdfe1ecd52 Set up modern performance APIs if the native module is available (#53431)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53431

Changelog: [internal]

This renames `setUpPerformanceObserver` as `setUpPerformanceModern` and removes the need to call it manually. If the native module is defined, we define the whole new API.

Reviewed By: javache

Differential Revision: D80803626

fbshipit-source-id: ef41cb9aa959ee898d32724c102d7597e6bee84e
2025-09-08 14:44:39 +01:00
Rubén Norte a65325b586 Implement private constructors for Performance APIs (#53430)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53430

Changelog: [internal]

This fixes the spec-compliance of several classes in the Performance API by not allowing userland code to instantiate them directly.

This also exposes some missing interfaces from the Performance API in the global scope.

Reviewed By: rshest

Differential Revision: D80800076

fbshipit-source-id: f6439b9c7914817ef552e78fd61646ccab1e1de2
2025-09-08 14:44:27 +01:00
Rubén Norte ccb34a1063 Refactor PerformanceEntry and subclasses to use interfaces for initialization (#53429)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53429

Changelog: [internal]

This is a refactor of the types in `PerformanceEntry` and subclasses to accept interfaces instead of objects. This allows us to pass down the init object from subclasses to the superclass without having to create intermediate objects.

Additionally, this is also more semantically correct, as existing APIs don't need those options to be own properties of the init object.

Existing benchmark for Performance doesn't show any significant impact.

Reviewed By: rshest

Differential Revision: D80800075

fbshipit-source-id: ab439d70f4db9ce60e3089d89ccb105a91e7ef48
2025-09-08 14:44:14 +01:00
Rubén Norte 68e774bb2c Implement PerformanceObserver.takeRecords() (#53428)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53428

Changelog: [internal]

This is the last method in `PerformanceObserver` to implement. For some reason we never added it, even though it was trivial.

Reviewed By: rshest

Differential Revision: D80717237

fbshipit-source-id: ae3bd243d0f3f0fe4f0705437d78d14c532515f7
2025-09-08 14:43:50 +01:00
Rubén Norte 1cacdf70a1 Remove unnecessary references to internal types in performance tests (#53427)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53427

Changelog: [internal]

Migrate the imported types to the globally defined ones, so we follow the good practice of only accessing the public API in Fantom tests.

Reviewed By: rshest

Differential Revision: D80807160

fbshipit-source-id: 77d792b56b53c8da8409dd9133cd111afb8084f1
2025-09-08 14:41:23 +01:00
Ramanpreet Nara 72d4a1cbd4 Deprecate legacy javascript apis (#53630)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53630

These JavaScript apis were a part of react native's legacy architecture. Let's deprecate them, so that we can eventually remove them in the future.

Changelog: [General][Deprecated] - Deprecate legacy javascript react native apis

Reviewed By: cortinico

Differential Revision: D81795732

fbshipit-source-id: 0a2bd142fa7e08c1f3daaa437ee127a2156e045b
2025-09-08 14:39:28 +01:00
Oskar Kwaśniewski 0cfd7230c6 fix: fallback alert controller to UIScreen size (#53500)
Summary:
This PR falls back to UIScreen when windowScene is not available.

<img width="500" alt="CleanShot 2025-08-28 at 14 30 59@2x" src="https://github.com/user-attachments/assets/9dda3153-dfe7-48a5-9d0e-5416c2e34c64" />

## Changelog:

[IOS] [FIXED] - Simplify RCTAlertController, don't create additional UIWindow

Pull Request resolved: https://github.com/facebook/react-native/pull/53500

Test Plan:
Open the alert multiple times to check if everything works as expected.

Rollback Plan:

Reviewed By: javache

Differential Revision: D81410450

Pulled By: cipolleschi

fbshipit-source-id: c27ea98d9e811c2f259f0ff3c6689482d116c418
2025-09-08 14:39:03 +01:00
Jakub Piasecki 4fdefa7193 Use hermesc from node_modules when consuming prebuilt hermes (#53581)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53581

Changelog: [General][Changed] - Changed the source of hermesc binary to be an npm package

Reviewed By: cipolleschi, cortinico

Differential Revision: D81224001

fbshipit-source-id: 552d0e66fb891974d7b688bfc0bec95e19345d86
2025-09-08 14:38:24 +01:00
Jakub Piasecki af734f0ccd Allow to opt-in to use the new Hermes on Android (#53580)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53580

Changelog: [ANDROID][ADDED] Added opt-in to use the new Hermes

Reviewed By: cortinico

Differential Revision: D81035114

fbshipit-source-id: d01e44190941d161cf641ec4e03ed487aff18dd8
2025-09-08 14:38:09 +01:00
Jakub Piasecki af090eaa69 Allow to opt-in to use the new Hermes on iOS (#53579)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53579

Changelog: [IOS][ADDED] Added opt-in to use the new Hermes

Reviewed By: cipolleschi

Differential Revision: D81035113

fbshipit-source-id: b12ca68824ec4e736edd4393a93c28803312eb32
2025-09-08 14:34:56 +01:00
Jakub Piasecki dc585b0f05 Gate legacy debugger behind a preprocessor directive (#53578)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53578

Changelog: [Internal]

Adds a new preprocessor directive which should be set when the new Hermes is being used. This directive will disable the legacy debugger which isn't supported by it.

Reviewed By: cipolleschi, cortinico

Differential Revision: D81035112

fbshipit-source-id: b30ae348b3419ec2d064dfe7f91c9d664a66f5cf
2025-09-08 14:34:46 +01:00
Phil Pluckthun 26cc647db3 Replace execSync with spawnSync for tarball extraction paths that need to be escaped (#53540)
Summary:
Follow-up to https://github.com/facebook/react-native/issues/53194

This wasn't previously visible in testing without prebuilds and without a release build. This doesn't show up in debug builds.

When testing more against paths that contain spaces, I noticed that release builds can still run into trouble due to the use of `execSync` without escaping paths. While, in other scripts that aren't used in user-projects (afaict), we often escape with quotes and rely on `execSync` calling the shell (due to its `shell: true` default), in some scripts we don't have quote escapes.

That said, since paths could in theory contain quotes, adding quotes wouldn't be sufficient. Instead, since the affected `tar` calls are really trivial, we can instead use `spawnSync` with the `shell: false` default, which escapes arguments automatically.

## Changelog:

[IOS] [FIXED] - fix Node scripts related to prebuilt tarball extraction for paths containing whitespaces

Pull Request resolved: https://github.com/facebook/react-native/pull/53540

Test Plan: - Create a project in a folder `with spaces` and build a release build

Reviewed By: cipolleschi, cortinico

Differential Revision: D81406841

Pulled By: robhogan

fbshipit-source-id: 08bb06b2cd2b15dc17c2f95fab9024129deca6f3
2025-09-08 14:33:39 +01:00
Riccardo Cipolleschi f194ee5626 Fix build with Cocopaods and Dynamic frameworks (#53367)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53367

We are missing a dependency in the React-jsinspector podspec that prevents React Native from building with dynamic frameworks.

## Changelog:
[Internal] -

Reviewed By: cortinico

Differential Revision: D80619664

fbshipit-source-id: 1c87ef4d3614ceea3a23196831479ecae0a5acc8
2025-09-08 14:33:19 +01:00
Phil Pluckthun b6c338780c Use autolinking react-native-config output in iOS artifacts generator (#53503)
Summary:
Resolves https://github.com/facebook/react-native/issues/53501

This is a pretty major oversight of (presumably) the old autolinking refactor. The iOS autolinking's second stage, invoked in `use_react_native!` does not accept the `react-native-config` sub-command's `react-native-config` output. This is only invoked and used in the prior step, `use_native_modules`.

The second step instead invokes old code that does something _similar_ to the new autolinking in `scripts/generate-artifacts-executor`, and happens to align in most cases. (But it does "autolinking" from scratch). tl;dr: When the results don't match up, things go wrong.

Instead, we now write the autolinking (react native config) results to a file, then read the output back in the second step.

This doesn't affect Android/Gradle, which are implemented correctly.

## Changelog:

[IOS] [FIXED] - Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source

Pull Request resolved: https://github.com/facebook/react-native/pull/53503

Test Plan:
- See https://github.com/facebook/react-native/issues/53501 for failing repro
- Clone for working repro: https://github.com/byCedric/react-native-codegen-ios-autolinking/tree/fix-54503
  - Note: Contains this PR's changes as a patch
  - `bun install`
  - `bun expo run:ios`

Reviewed By: cortinico

Differential Revision: D81490755

Pulled By: cipolleschi

fbshipit-source-id: eefe786a116404f4ed24bd7125dfb108a811f71e
2025-09-08 14:32:59 +01:00
Nicola Corti f49ae8a388 Fix build from source for 0.82 due to Gradle 9.0 (#53560)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53560

Since Gradle 9.0, all the projects in the path must have an existing folder.
As we build :packages:react-native:ReactAndroid, we need to declare the folders
for :packages and :packages:react-native as well as otherwise the build from
source will fail with a missing folder exception.

Changelog:
[Android] [Fixed] - Fix build from source due to missing folder error on Gradle 9.0

Reviewed By: fabriziocucci

Differential Revision: D81482789

fbshipit-source-id: 609b503755486e10060a0f321bd0a38bd71864a1
2025-09-08 14:32:28 +01:00
Moti Zilberman fdc4da96e4 Distribute React Native DevTools binaries via GitHub Releases (#52930) (#53604)
Summary:
bypass-github-export-checks

OSS release infrastructure for the (experimental) React Native DevTools standalone shell.

Currently, binaries are built continuously on Meta infra and served from the Meta CDN using fbcdn.net URLs checked into a DotSlash file in the repo, e.g.:

https://github.com/facebook/react-native/blob/15373218ec572c0e43325845b80a849ad5174cc3/packages/debugger-shell/bin/react-native-devtools#L9-L18

For open source releases we want to primarily distribute the binaries as GitHub release assets, while keeping the Meta CDN URLs as a secondary option. This PR makes the necessary changes to the release workflows to support this:

* `workflows/create-release.yml` (modified): As part of the release commit, rewrite the DotSlash file to include the release asset URLs.
  * **NOTE:** After this commit, **the new URLs don't work yet**, because they refer to a release that hasn't been published. Despite this, the DotSlash file remains valid and usable (because DotSlash will happily fall back to the Meta CDN URLs, which are still in the file).
* `workflows/create-draft-release.yml` (modified): After creating a draft release, fetch the binaries from the Meta CDN and reupload them to GitHub as release assets. This is based on the contents of the DotSlash file rewritten by `create-release.yml`.
* `workflows/validate-dotslash-artifacts.yml` (new): After the release is published, all URLs referenced by the DotSlash (both Meta CDN URL and GH release asset URLs) should be valid and refer to the same artifacts. This workflow checks that this is the case.
  * If this workflow fails on a published release, the release may need to be burned or a hotfix release may be necessary - as the release will stop working correctly once the Meta CDN stops serving the assets.
  * This workflow will also be running continuously on `main`. If it fails on a commit in `main`, there might be a connectivity issue between the GHA runner and the Meta CDN, or there might be an issue on the Meta side.

NOTE: These changes to the release pipeline are generic and reusable; if we later add another DotSlash-based tool whose binaries need to be mirrored as GitHub release assets, we just need to add it to the `FIRST_PARTY_DOTSLASH_FILES` array.

[Internal] Mirror React Native DevTools binaries in GitHub Releases

Pull Request resolved: https://github.com/facebook/react-native/pull/52930

Test Plan:

I've added unit tests for `dotslash-utils`, `curl-utils`, and for the majority of the logic that makes up the new release scripts (`write-dotslash-release-assets-urls`, `upload-release-assets-for-dotslash`, `validate-dotslash-artifacts`).

Created a test branch and draft PR: https://github.com/facebook/react-native/pull/53147.

Locally created a release commit, simulating the create-release GH workflow:

```
node scripts/releases/create-release-commit.js --reactNativeVersion 0.82.0-20250903-0830 --no-dry-run
```

This updated the DotSlash file in the branch: https://github.com/facebook/react-native/pull/53147/commits/2deeb7e70376ee80b99f27bea4825789f22a89a3#diff-205a9ff6005e30be061eaa64b9cb50b15b0e909dd188e0866189e952655a3483

NOTE: I've also ensured that the `create-release-commit` script correctly updates the DotSlash file when running from a branch that already has a release commit - see screenshot:
<img width="1483" height="587" alt="image" src="https://github.com/user-attachments/assets/1ffd859b-e02b-483d-8067-9cc9116829a4" />

Enabled testing the create-draft-release GH workflow in the test branch using these temporary hacks:

* https://github.com/facebook/react-native/pull/53147/commits/81f334eac5147d4dbf5f6d7d627ddfa52cd197be
* https://github.com/facebook/react-native/pull/53147/commits/6d8851657629de7e0b710ed8f5dd7d0f7b9847cc
* https://github.com/facebook/react-native/pull/53147/commits/1428a8da8b9fb29c45fc33d79f311dd1fe273433

Workflow run: https://github.com/facebook/react-native/actions/runs/17426711373/job/49475327346
Draft release: https://github.com/facebook/react-native/releases/tag/untagged-c6a62a58e5baa37936e1
Draft release screenshot for posterity (since we'll likely delete the draft release after landing this):

<img width="1024" height="814" alt="image" src="https://github.com/user-attachments/assets/1900da15-48f6-4274-b29c-0ac2019d92c0" />

For obvious reasons, I've avoided actually publishing the above draft release. But I have run the `validate-dotslash-artifacts` workflow on the *current* branch to ensure that the logic is correct: https://github.com/motiz88/react-native/actions/runs/17426885205/job/49475888486

Running `node scripts/releases/validate-dotslash-artifacts.js` in the release branch (without publishing the release first) fails, as expected:

<img width="1105" height="748" alt="image" src="https://github.com/user-attachments/assets/ed23a2e2-7a31-42eb-a324-f1d50eafe2fb" />

This PR is all the infra needed ahead of the 0.82 ~~branch cut~~ infra freeze to support the React Native DevTools standalone shell, at least on the GitHub side. ~~Some minor infra work remains on the Meta side, plus some product/logic changes to the React Native DevTools standalone shell that I'm intending to finish in time for 0.82 (for an experimental rollout).~~ EDIT: All the planned work has landed; the feature is code-complete on `main` as well as in `0.82-stable` (apart from this infra change).

As a one-off, once we've actually published 0.82.0-rc.1, we'll want to have a human look at the published artifacts and CI workflow logs to ensure everything is in order. (I'll make sure to communicate this to the 0.82 release crew.) Afterwards, the automation added in this PR should be sufficient.

Reviewed By: huntie

Differential Revision: D81578704

Pulled By: motiz88

fbshipit-source-id: 6a4a48c3713221a89dd5fc88851674c1ddc6bb10
2025-09-08 14:19:54 +01:00
Vitali Zaidman e5647a18a8 Update Podfile.lock
Changelog: [Internal]
2025-09-02 13:08:14 +01:00
React Native Bot 044d07c670 Release 0.82.0-rc.0
#publish-packages-to-npm&next
2025-09-02 10:05:17 +00:00
Vitali Zaidman 1b359eefba fixed artifacts not found in release-testing script 2025-09-01 18:28:54 +01:00
Vitali Zaidman 92b33c986c Bump hermes version 2025-09-01 16:43:10 +01:00
131 changed files with 4605 additions and 689 deletions
@@ -188,6 +188,7 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
status: 201,
json: () =>
Promise.resolve({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
}),
@@ -208,9 +209,11 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
body: fetchBody,
},
);
expect(response).toEqual(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
expect(response).toEqual({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
});
});
it('creates a draft release for prerelease on GitHub', async () => {
@@ -238,6 +241,7 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
status: 201,
json: () =>
Promise.resolve({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
}),
@@ -258,9 +262,11 @@ View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/
body: fetchBody,
},
);
expect(response).toEqual(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
expect(response).toEqual({
id: 1,
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
});
});
it('throws if the post failes', async () => {
@@ -101,7 +101,11 @@ async function _createDraftReleaseOnGitHub(version, body, latest, token) {
}
const data = await response.json();
return data.html_url;
const {html_url, id} = data;
return {
html_url,
id,
};
}
function moveToChangelogBranch(version) {
@@ -124,7 +128,8 @@ async function createDraftRelease(version, latest, token) {
latest,
token,
);
log(`Created draft release: ${release}`);
log(`Created draft release: ${release.html_url}, ID ${release.id}`);
return release;
}
module.exports = {
+16 -1
View File
@@ -21,9 +21,24 @@ jobs:
git config --local user.name "React Native Bot"
- name: Create draft release
uses: actions/github-script@v6
id: create-draft-release
with:
script: |
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
const version = '${{ github.ref_name }}';
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}');
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}')).id;
result-encoding: string
- name: Upload release assets for DotSlash
uses: actions/github-script@v6
env:
RELEASE_ID: ${{ steps.create-draft-release.outputs.result }}
with:
script: |
const {uploadReleaseAssetsForDotSlashFiles} = require('./scripts/releases/upload-release-assets-for-dotslash.js');
const version = '${{ github.ref_name }}';
await uploadReleaseAssetsForDotSlashFiles({
version,
token: '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}',
releaseId: process.env.RELEASE_ID,
});
+2 -1
View File
@@ -273,7 +273,8 @@ jobs:
NEW_ARCH_ENABLED=1
export RCT_USE_LOCAL_RN_DEP=/tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
# Disable prebuilds for now, as they are causing issues with E2E tests for 0.82-stable branch
# export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
xcodebuild \
@@ -0,0 +1,48 @@
name: Validate DotSlash Artifacts
on:
workflow_dispatch:
release:
types: [published]
push:
branches:
- main
paths:
- packages/debugger-shell/bin/react-native-devtools
- "scripts/releases/**"
- package.json
- yarn.lock
pull_request:
branches:
- main
paths:
- packages/debugger-shell/bin/react-native-devtools
- "scripts/releases/**"
- package.json
- yarn.lock
# Same time as the nightly build: 2:15 AM UTC
schedule:
- cron: "15 2 * * *"
jobs:
validate-dotslash-artifacts:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Configure Git
shell: bash
run: |
git config --local user.email "bot@reactnative.dev"
git config --local user.name "React Native Bot"
- name: Validate DotSlash artifacts
uses: actions/github-script@v6
with:
script: |
const {validateDotSlashArtifacts} = require('./scripts/releases/validate-dotslash-artifacts.js');
await validateDotSlashArtifacts();
+46
View File
@@ -0,0 +1,46 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare module '@expo/spawn-async' {
type SpawnOptions = {
cwd?: string,
env?: Object,
argv0?: string,
stdio?: string | Array<any>,
detached?: boolean,
uid?: number,
gid?: number,
shell?: boolean | string,
windowsVerbatimArguments?: boolean,
windowsHide?: boolean,
encoding?: string,
ignoreStdio?: boolean,
};
declare class SpawnPromise<T> extends Promise<T> {
child: child_process$ChildProcess;
}
type SpawnResult = {
pid?: number,
output: string[],
stdout: string,
stderr: string,
status: number | null,
signal: string | null,
};
declare function spawnAsync(
command: string,
args?: $ReadOnlyArray<string>,
options?: SpawnOptions,
): SpawnPromise<SpawnResult>;
declare module.exports: typeof spawnAsync;
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
// Partial types for Octokit based on the usage in react-native-github
declare module '@octokit/rest' {
declare class Octokit {
constructor(options?: {auth?: string, ...}): this;
repos: $ReadOnly<{
listReleaseAssets: (
params: $ReadOnly<{
owner: string,
repo: string,
release_id: string,
}>,
) => Promise<{
data: Array<{
id: string,
name: string,
...
}>,
...
}>,
uploadReleaseAsset: (
params: $ReadOnly<{
owner: string,
repo: string,
release_id: string,
name: string,
data: Buffer,
headers: $ReadOnly<{
'content-type': string,
...
}>,
...
}>,
) => Promise<{
data: {
browser_download_url: string,
...
},
...
}>,
deleteReleaseAsset: (params: {
owner: string,
repo: string,
asset_id: string,
...
}) => Promise<mixed>,
}>;
}
declare export {Octokit};
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare module 'fb-dotslash' {
declare module.exports: string;
}
+421
View File
@@ -0,0 +1,421 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare module 'jsonc-parser' {
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
declare export const createScanner: (
text: string,
ignoreTrivia?: boolean,
) => JSONScanner;
export type ScanError = number;
export type SyntaxKind = number;
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export type JSONScanner = $ReadOnly<{
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): void,
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind,
/**
* Returns the zero-based current scan position, which is after the last read token.
*/
getPosition(): number,
/**
* Returns the last read token.
*/
getToken(): SyntaxKind,
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string,
/**
* The zero-based start offset of the last read token.
*/
getTokenOffset(): number,
/**
* The length of the last read token.
*/
getTokenLength(): number,
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number,
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number,
/**
* An error code of the last scan.
*/
getTokenError(): ScanError,
}>;
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
declare export const getLocation: (
text: string,
position: number,
) => Location;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
declare export const parse: (
text: string,
errors?: ParseError[],
options?: ParseOptions,
) => any;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
declare export const parseTree: (
text: string,
errors?: ParseError[],
options?: ParseOptions,
) => Node | void;
/**
* Finds the node at the given path in a JSON DOM.
*/
declare export const findNodeAtLocation: (
root: Node,
path: JSONPath,
) => Node | void;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
declare export const findNodeAtOffset: (
root: Node,
offset: number,
includeRightBound?: boolean,
) => Node | void;
/**
* Gets the JSON path of the given JSON DOM node
*/
declare export const getNodePath: (node: Node) => JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
declare export const getNodeValue: (node: Node) => any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
declare export const visit: (
text: string,
visitor: JSONVisitor,
options?: ParseOptions,
) => any;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
declare export const stripComments: (
text: string,
replaceCh?: string,
) => string;
export type ParseError = {
error: ParseErrorCode,
offset: number,
length: number,
};
export type ParseErrorCode = number;
declare export function printParseErrorCode(
code: ParseErrorCode,
):
| 'InvalidSymbol'
| 'InvalidNumberFormat'
| 'PropertyNameExpected'
| 'ValueExpected'
| 'ColonExpected'
| 'CommaExpected'
| 'CloseBraceExpected'
| 'CloseBracketExpected'
| 'EndOfFileExpected'
| 'InvalidCommentToken'
| 'UnexpectedEndOfComment'
| 'UnexpectedEndOfString'
| 'UnexpectedEndOfNumber'
| 'InvalidUnicode'
| 'InvalidEscapeCharacter'
| 'InvalidCharacter'
| '<unknown ParseErrorCode>';
export type NodeType =
| 'object'
| 'array'
| 'property'
| 'string'
| 'number'
| 'boolean'
| 'null';
export type Node = {
type: NodeType,
value?: any,
offset: number,
length: number,
colonOffset?: number,
parent?: Node,
children?: Node[],
};
/**
* A {@linkcode JSONPath} segment. Either a string representing an object property name
* or a number (starting at 0) for array indices.
*/
export type Segment = string | number;
export type JSONPath = Segment[];
export type Location = {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node,
/**
* The path describing the location in the JSON document. The path consists of a sequence of strings
* representing an object property or numbers for array indices.
*/
path: JSONPath,
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment of any property name or index.
* '**' will match a sequence of segments of any property name or index, or no segment.
*/
matches: (patterns: JSONPath) => boolean,
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean,
};
export type ParseOptions = {
disallowComments?: boolean,
allowTrailingComma?: boolean,
allowEmptyContent?: boolean,
};
/**
* Visitor called by {@linkcode visit} when parsing JSON.
*
* The visitor functions have the following common parameters:
* - `offset`: Global offset within the JSON document, starting at 0
* - `startLine`: Line number, starting at 0
* - `startCharacter`: Start character (column) within the current line, starting at 0
*
* Additionally some functions have a `pathSupplier` parameter which can be used to obtain the
* current `JSONPath` within the document.
*/
export type JSONVisitor = {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
*/
onObjectBegin?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
* The `JSONPath` created by the `pathSupplier` refers to the enclosing JSON object, it does not include the
* property name yet.
*/
onObjectProperty?: (
property: string,
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
*/
onArrayBegin?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (
value: any,
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath,
) => void,
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (
character: string,
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
/**
* Invoked on an error.
*/
onError?: (
error: ParseErrorCode,
offset: number,
length: number,
startLine: number,
startCharacter: number,
) => void,
};
/**
* An edit result describes a textual edit operation. It is the result of a {@linkcode format} and {@linkcode modify} operation.
* It consist of one or more edits describing insertions, replacements or removals of text segments.
* * The offsets of the edits refer to the original state of the document.
* * No two edits change or remove the same range of text in the original document.
* * Multiple edits can have the same offset if they are multiple inserts, or an insert followed by a remove or replace.
* * The order in the array defines which edit is applied first.
* To apply an edit result use {@linkcode applyEdits}.
* In general multiple EditResults must not be concatenated because they might impact each other, producing incorrect or malformed JSON data.
*/
export type EditResult = Edit[];
/**
* Represents a text modification
*/
export type Edit = {
/**
* The start offset of the modification.
*/
offset: number,
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number,
/**
* The new content. Empty content represents a *remove*.
*/
content: string,
};
/**
* A text range in the document
*/
export type Range = {
/**
* The start offset of the range.
*/
offset: number,
/**
* The length of the range. Must not be negative.
*/
length: number,
};
/**
* Options used by {@linkcode format} when computing the formatting edit operations
*/
export type FormattingOptions = $ReadOnly<{
/**
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
*/
tabSize?: number,
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean,
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string,
}>;
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
declare export function format(
documentText: string,
range: Range | void,
options: FormattingOptions,
): EditResult;
/**
* Options used by {@linkcode modify} when computing the modification edit operations
*/
export type ModificationOptions = {
/**
* Formatting options.
*/
formattingOptions: FormattingOptions,
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number,
};
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
declare export function modify(
text: string,
path: JSONPath,
value: any,
options: ModificationOptions,
): EditResult;
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
declare export function applyEdits(text: string, edits: EditResult): string;
}
+3
View File
@@ -12,3 +12,6 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Controls whether to use Hermes from nightly builds. This will speed up builds
# but should NOT be turned on for CI or release builds.
react.internal.useHermesNightly=false
# Controls whether to use Hermes 1.0. Clean and rebuild when changing.
hermesV1Enabled=false
+7 -2
View File
@@ -54,13 +54,16 @@
"@babel/preset-env": "^7.25.3",
"@babel/preset-flow": "^7.24.7",
"@electron/packager": "^18.3.6",
"@expo/spawn-async": "^1.7.2",
"@jest/create-cache-key-function": "^29.7.0",
"@microsoft/api-extractor": "^7.52.2",
"@react-native/metro-babel-transformer": "0.82.0-main",
"@react-native/metro-config": "0.82.0-main",
"@octokit/rest": "^22.0.0",
"@react-native/metro-babel-transformer": "0.82.0-rc.1",
"@react-native/metro-config": "0.82.0-rc.1",
"@tsconfig/node22": "22.0.2",
"@types/react": "^19.1.0",
"@typescript-eslint/parser": "^8.36.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.2.1",
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
"babel-plugin-syntax-hermes-parser": "0.32.0",
@@ -81,6 +84,7 @@
"eslint-plugin-react-native": "^4.0.0",
"eslint-plugin-redundant-undefined": "^0.4.0",
"eslint-plugin-relay": "^1.8.3",
"fb-dotslash": "0.5.8",
"flow-api-translator": "0.32.0",
"flow-bin": "^0.280.0",
"glob": "^7.1.1",
@@ -93,6 +97,7 @@
"jest-diff": "^29.7.0",
"jest-junit": "^16.0.0",
"jest-snapshot": "^29.7.0",
"jsonc-parser": "2.2.1",
"markdownlint-cli2": "^0.17.2",
"markdownlint-rule-relative-links": "^3.0.0",
"memfs": "^4.7.7",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Asset support code for React Native.",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-plugin-codegen",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.82.0-rc.1"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,7 +22,7 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.82.0-main",
"@react-native/dev-middleware": "0.82.0-rc.1",
"debug": "^4.4.0",
"invariant": "^2.2.4",
"metro": "^0.83.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Debugger frontend for React Native based on Chrome DevTools",
"keywords": [
"react-native",
@@ -1,6 +1,6 @@
#!/usr/bin/env dotslash
// @generated SignedSource<<e93d55b5e28943e44271f5d3738083e0>>
// @generated SignedSource<<f413a95849f40718f06ee03ed49972bb>>
{
@@ -11,6 +11,9 @@
"hash": "sha256",
"digest": "4352f1c9848ca919101ec628bd08b87a72a828d1ab55fa43a02098329fa452fa",
"providers": [
{
"url": "https://github.com/facebook/react-native/releases/download/v0.82.0-rc.1/React.Native.DevTools-linux-aarch64.tar.gz"
},
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQM24LW0nPZRk0ypuIG9prz_72YjBJNcVlGSIEpO4zdlLXgw4dFNodH7MKg9eKNTnx7wrVDDBNACrnEPt_OfXOjZyZsV9Oaqu0-vNFRdlEyis4YqmpqGLtz3LvD-9R6fzcWxJI9zrhdPvOvlXP-3Syt1UNITaxXDVqIwAAYpCaAh0oLpupAFCc2yvYw"
@@ -24,6 +27,9 @@
"hash": "sha256",
"digest": "11c7b07942928a6301b07fbf2bc77ce1229b2a52891f23541cdd9858b5250e64",
"providers": [
{
"url": "https://github.com/facebook/react-native/releases/download/v0.82.0-rc.1/React.Native.DevTools-linux-x86_64.tar.gz"
},
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQNDwm7HZRhtNxHqMr1FfSb0afHFGrn1OHxH0gOiggrLrht9QRUgJ3GG5jj7huhQzMRogE-LCMsnxh1ioOZks-YYX4KRt6Kj1-whdWsGFc7lBhPOpk1ssbYFGN1NNyuyFRmH-3nCY3lBC4AmbCUkbDTUeCi9DidCtJeyc73CZJEu7M62rIzxR2yV"
@@ -37,6 +43,9 @@
"hash": "sha256",
"digest": "3cbe8b1b3d17e433347f1601435bb9a6cb758528a5c176a66fc52d9977223175",
"providers": [
{
"url": "https://github.com/facebook/react-native/releases/download/v0.82.0-rc.1/React.Native.DevTools-macos-aarch64.tar.gz"
},
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOmC2cqqSv4OrSJKJroYVg_NE8OE4O73AXqY7wXiYqiWQVkDt0Xnyw3ZeUpQT_Qb0-OoT5F8REKoFrB6eqwat8Ovkyina30peYTTwNUzmwnnGQEg7J0fOHNxLF4dkmU1FagXtsoWgex4dKgsK_VpcMsHj3Vp7diomkYvWBVTf_gPVEseYSN9oKq92qa"
@@ -50,6 +59,9 @@
"hash": "sha256",
"digest": "6fb79bc2ba3008401b4c9c128248657b95b98581ccde60f8fadb622163779775",
"providers": [
{
"url": "https://github.com/facebook/react-native/releases/download/v0.82.0-rc.1/React.Native.DevTools-macos-x86_64.tar.gz"
},
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQMMtGn-YGdfLfTVWC8zbQkQx65Asq6iArKt1t__cjZ8UY_s6-sX5XBHr8k1SaexAO21dFZENQVZ1jW_wn_gJ9ENvosQDG1KfWMViKsHli0xRzZ1HVsgPIj_KVXe907QZwwtJf2XhgH0HT8dfH-AQdDcd0_TB5DFUwOsHzhH0nBrHet7YFkbJtPTaA"
@@ -63,6 +75,9 @@
"hash": "sha256",
"digest": "579a5b0944c51c3b1b541ad5af66c1ffedf93cae2a891ecdf88cb7219fd9b096",
"providers": [
{
"url": "https://github.com/facebook/react-native/releases/download/v0.82.0-rc.1/React.Native.DevTools-windows-x86_64.tar.gz"
},
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOQ3E8lBXqdVHbDPyVb5AOQMrDWjFrFV8fnLLBsygQvLWdpu6ixyG9PgWdwpi5jM-XcDdCHkhBdhaq-5dwT_tgRWKCAMsEBoAIUk0Xg77mGyHG2VF7bNfQ2qFBMuObrsTmrKy1nJ-UFDDm29pJD4GkFQW5NesiBwndJj8t3B8Ur8cczh_XR8rF5"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-shell",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Experimental debugger shell for React Native for use with @react-native/debugger-frontend",
"keywords": [
"react-native",
@@ -152,11 +152,7 @@ function getShellBinaryAndArgs(
): [string, Array<string>] {
switch (flavor) {
case 'prebuilt':
return [
// $FlowFixMe[cannot-resolve-module] fb-dotslash includes Flow types but Flow does not pick them up
require('fb-dotslash'),
[DEVTOOLS_BINARY_DOTSLASH_FILE],
];
return [require('fb-dotslash'), [DEVTOOLS_BINARY_DOTSLASH_FILE]];
case 'dev':
return [
// NOTE: Internally at Meta, this is aliased to a workspace that is
@@ -44,11 +44,11 @@ async function spawnAndGetStderr(
async function prepareDebuggerShellFromDotSlashFile(
filePath: string,
): Promise<DebuggerShellPreparationResult> {
const {code, stderr} = await spawnAndGetStderr(
// $FlowFixMe[cannot-resolve-module] fb-dotslash includes Flow types but Flow does not pick them up
require('fb-dotslash'),
['--', 'fetch', filePath],
);
const {code, stderr} = await spawnAndGetStderr(require('fb-dotslash'), [
'--',
'fetch',
filePath,
]);
if (code === 0) {
return {code: 'success'};
}
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/dev-middleware",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Dev server middleware for React Native",
"keywords": [
"react-native",
@@ -23,8 +23,8 @@
],
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.82.0-main",
"@react-native/debugger-shell": "0.82.0-main",
"@react-native/debugger-frontend": "0.82.0-rc.1",
"@react-native/debugger-shell": "0.82.0-rc.1",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
@@ -39,7 +39,7 @@
"node": ">= 20.19.4"
},
"devDependencies": {
"@react-native/debugger-shell": "0.82.0-main",
"@react-native/debugger-shell": "0.82.0-rc.1",
"selfsigned": "^2.4.1",
"undici": "^5.29.0",
"wait-for-expect": "^3.0.2"
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "ESLint config for React Native",
"license": "MIT",
"repository": {
@@ -22,7 +22,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@react-native/eslint-plugin": "0.82.0-main",
"@react-native/eslint-plugin": "0.82.0-rc.1",
"@typescript-eslint/eslint-plugin": "^8.36.0",
"@typescript-eslint/parser": "^8.36.0",
"eslint-config-prettier": "^8.5.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "ESLint rules for @react-native/eslint-config",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin-specs",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@react-native/codegen": "0.82.0-main",
"@react-native/codegen": "0.82.0-rc.1",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/gradle-plugin",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
@@ -28,6 +28,7 @@ import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import com.facebook.react.utils.findPackageJsonFile
import java.io.File
@@ -54,6 +55,10 @@ class ReactPlugin : Plugin<Project> {
project,
)
if (project.rootProject.isHermesV1Enabled != rootExtension.hermesV1Enabled.get()) {
rootExtension.hermesV1Enabled.set(project.rootProject.isHermesV1Enabled)
}
// App Only Configuration
project.pluginManager.withPlugin("com.android.application") {
// We wire the root extension with the values coming from the app (either user populated or
@@ -67,9 +72,11 @@ class ReactPlugin : Plugin<Project> {
val reactNativeDir = extension.reactNativeDir.get().asFile
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
val versionString = versionAndGroupStrings.first
val groupString = versionAndGroupStrings.second
configureDependencies(project, versionString, groupString)
val hermesV1Enabled =
if (project.rootProject.hasProperty("hermesV1Enabled"))
project.rootProject.findProperty("hermesV1Enabled") == "true"
else false
configureDependencies(project, versionAndGroupStrings, hermesV1Enabled)
configureRepositories(project)
}
@@ -11,6 +11,7 @@ import javax.inject.Inject
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
/**
* A private extension we set on the rootProject to make easier to share values at execution time
@@ -57,4 +58,6 @@ abstract class PrivateReactExtension @Inject constructor(project: Project) {
val codegenDir: DirectoryProperty =
objects.directoryProperty().convention(root.dir("node_modules/@react-native/codegen"))
val hermesV1Enabled: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
}
@@ -94,7 +94,12 @@ abstract class BundleHermesCTask : DefaultTask() {
runCommand(bundleCommand)
if (hermesEnabled.get()) {
val detectedHermesCommand = detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get())
val hermesV1Enabled =
if (project.rootProject.hasProperty("hermesV1Enabled"))
project.rootProject.findProperty("hermesV1Enabled") == "true"
else false
val detectedHermesCommand =
detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled)
val bytecodeFile = File("${bundleFile}.hbc")
val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
@@ -7,12 +7,15 @@
package com.facebook.react.utils
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.EXCLUSIVE_ENTEPRISE_REPOSITORY
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT
import com.facebook.react.utils.PropertyUtils.INTERNAL_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_VERSION_NAME
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY
import com.facebook.react.utils.PropertyUtils.INTERNAL_VERSION_NAME
import com.facebook.react.utils.PropertyUtils.SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY
@@ -25,6 +28,13 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository
internal object DependencyUtils {
internal data class Coordinates(
val versionString: String,
val hermesVersionString: String,
val reactGroupString: String = DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP,
val hermesGroupString: String = DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP,
)
/**
* This method takes care of configuring the repositories{} block for both the app and all the 3rd
* party libraries which are auto-linked.
@@ -95,14 +105,15 @@ internal object DependencyUtils {
* This method takes care of configuring the resolution strategy for both the app and all the 3rd
* party libraries which are auto-linked. Specifically it takes care of:
* - Forcing the react-android/hermes-android version to the one specified in the package.json
* - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`.
* - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`
* - Selecting between the classic Hermes and Hermes V1
*/
fun configureDependencies(
project: Project,
versionString: String,
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
coordinates: Coordinates,
hermesV1Enabled: Boolean = false,
) {
if (versionString.isBlank()) return
if (coordinates.versionString.isBlank() || coordinates.hermesVersionString.isBlank()) return
project.rootProject.allprojects { eachProject ->
eachProject.configurations.all { configuration ->
// Here we set a dependencySubstitution for both react-native and hermes-engine as those
@@ -110,53 +121,61 @@ internal object DependencyUtils {
// This allows users to import libraries that are still using
// implementation("com.facebook.react:react-native:+") and resolve the right dependency.
configuration.resolutionStrategy.dependencySubstitution {
getDependencySubstitutions(versionString, groupString).forEach { (module, dest, reason) ->
getDependencySubstitutions(coordinates, hermesV1Enabled).forEach { (module, dest, reason)
->
it.substitute(it.module(module)).using(it.module(dest)).because(reason)
}
}
configuration.resolutionStrategy.force(
"${groupString}:react-android:${versionString}",
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
)
if (!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY) as? String).toBoolean()) {
// Contributors only: The hermes-engine version is forced only if the user has
// not opted into using nightlies for local development.
configuration.resolutionStrategy.force("${groupString}:hermes-android:${versionString}")
configuration.resolutionStrategy.force(
"${coordinates.reactGroupString}:hermes-android:${coordinates.versionString}"
)
}
}
}
}
internal fun getDependencySubstitutions(
versionString: String,
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
coordinates: Coordinates,
hermesV1Enabled: Boolean = false,
): List<Triple<String, String, String>> {
// TODO: T231755027 update coordinates and versioning
val dependencySubstitution = mutableListOf<Triple<String, String, String>>()
val hermesVersionString =
if (hermesV1Enabled)
"${coordinates.hermesGroupString}:hermes-android:${coordinates.versionString}"
else "${coordinates.reactGroupString}:hermes-android:${coordinates.versionString}"
dependencySubstitution.add(
Triple(
"com.facebook.react:react-native",
"${groupString}:react-android:${versionString}",
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.",
)
)
dependencySubstitution.add(
Triple(
"com.facebook.react:hermes-engine",
"${groupString}:hermes-android:${versionString}",
hermesVersionString,
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.",
)
)
if (groupString != DEFAULT_INTERNAL_PUBLISHING_GROUP) {
if (coordinates.reactGroupString != DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP) {
dependencySubstitution.add(
Triple(
"com.facebook.react:react-android",
"${groupString}:react-android:${versionString}",
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
"The react-android dependency was modified to use the correct Maven group.",
)
)
dependencySubstitution.add(
Triple(
"com.facebook.react:hermes-android",
"${groupString}:hermes-android:${versionString}",
hermesVersionString,
"The hermes-android dependency was modified to use the correct Maven group.",
)
)
@@ -164,10 +183,14 @@ internal object DependencyUtils {
return dependencySubstitution
}
fun readVersionAndGroupStrings(propertiesFile: File): Pair<String, String> {
fun readVersionAndGroupStrings(propertiesFile: File): Coordinates {
val reactAndroidProperties = Properties()
propertiesFile.inputStream().use { reactAndroidProperties.load(it) }
val versionStringFromFile = (reactAndroidProperties[INTERNAL_VERSION_NAME] as? String).orEmpty()
// TODO: T231755027 update HERMES_VERSION_NAME in gradle.properties to point to the correct
// hermes version
val hermesVersionStringFromFile =
(reactAndroidProperties[INTERNAL_HERMES_VERSION_NAME] as? String).orEmpty()
// If on a nightly, we need to fetch the -SNAPSHOT artifact from Sonatype.
val versionString =
if (versionStringFromFile.startsWith("0.0.0") || "-nightly-" in versionStringFromFile) {
@@ -176,10 +199,18 @@ internal object DependencyUtils {
versionStringFromFile
}
// Returns Maven group for repos using different group for Maven artifacts
val groupString =
reactAndroidProperties[INTERNAL_PUBLISHING_GROUP] as? String
?: DEFAULT_INTERNAL_PUBLISHING_GROUP
return Pair(versionString, groupString)
val reactGroupString =
reactAndroidProperties[INTERNAL_REACT_PUBLISHING_GROUP] as? String
?: DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP
val hermesGroupString =
reactAndroidProperties[INTERNAL_HERMES_PUBLISHING_GROUP] as? String
?: DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
return Coordinates(
versionString,
hermesVersionStringFromFile,
reactGroupString,
hermesGroupString,
)
}
fun Project.mavenRepoFromUrl(
@@ -122,11 +122,16 @@ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): F
* used if the user is building Hermes from source.
* 3. The file located in `node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc` where `%OS-BIN%`
* is substituted with the correct OS arch. This will be used if the user is using a precompiled
* hermes-engine package.
* hermes-engine package. Or, if the user has opted in to use Hermes V1, the used file will be
* located in `node_modules/hermes-compiler/%OS-BIN%/hermesc` where `%OS-BIN%` is substituted
* with the correct OS arch.
* 4. Fails otherwise
*/
internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String): String {
// 1. If the project specifies a Hermes command, don't second guess it.
internal fun detectOSAwareHermesCommand(
projectRoot: File,
hermesCommand: String,
hermesV1Enabled: Boolean = false,
): String { // 1. If the project specifies a Hermes command, don't second guess it.
if (hermesCommand.isNotBlank()) {
val osSpecificHermesCommand =
if ("%OS-BIN%" in hermesCommand) {
@@ -146,9 +151,12 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String
return builtHermesc.cliPath(projectRoot)
}
// 3. If the react-native contains a pre-built hermesc, use it.
// 3. If Hermes V1 is enabled, use hermes-compiler from npm, otherwise, if the
// react-native contains a pre-built hermesc, use it.
val hermesCPath = if (hermesV1Enabled) HERMES_COMPILER_NPM_DIR else HERMESC_IN_REACT_NATIVE_DIR
val prebuiltHermesPath =
HERMESC_IN_REACT_NATIVE_DIR.plus(getHermesCBin())
hermesCPath
.plus(getHermesCBin())
.replace("%OS-BIN%", getHermesOSBin())
// Execution on Windows fails with / as separator
.replace('/', File.separatorChar)
@@ -233,6 +241,7 @@ internal fun readPackageJsonFile(
return packageJson?.let { JsonUtils.fromPackageJson(it) }
}
private const val HERMES_COMPILER_NPM_DIR = "node_modules/hermes-compiler/%OS-BIN%/"
private const val HERMESC_IN_REACT_NATIVE_DIR = "node_modules/react-native/sdks/hermesc/%OS-BIN%/"
private const val HERMESC_BUILT_FROM_SOURCE_DIR =
"node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/"
@@ -13,9 +13,11 @@ import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
import com.facebook.react.utils.PropertyUtils.EDGE_TO_EDGE_ENABLED
import com.facebook.react.utils.PropertyUtils.HERMES_ENABLED
import com.facebook.react.utils.PropertyUtils.HERMES_V1_ENABLED
import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES
import com.facebook.react.utils.PropertyUtils.SCOPED_EDGE_TO_EDGE_ENABLED
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_V1_ENABLED
import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES
import com.facebook.react.utils.PropertyUtils.SCOPED_USE_THIRD_PARTY_JSC
import com.facebook.react.utils.PropertyUtils.USE_THIRD_PARTY_JSC
@@ -68,6 +70,13 @@ internal object ProjectUtils {
(project.hasProperty(SCOPED_USE_THIRD_PARTY_JSC) &&
project.property(SCOPED_USE_THIRD_PARTY_JSC).toString().toBoolean())
internal val Project.isHermesV1Enabled: Boolean
get() =
(project.hasProperty(HERMES_V1_ENABLED) &&
project.property(HERMES_V1_ENABLED).toString().toBoolean()) ||
(project.hasProperty(SCOPED_HERMES_V1_ENABLED) &&
project.property(SCOPED_HERMES_V1_ENABLED).toString().toBoolean())
internal fun Project.needsCodegenFromPackageJson(rootProperty: DirectoryProperty): Boolean {
val parsedPackageJson = readPackageJsonFile(this, rootProperty)
return needsCodegenFromPackageJson(parsedPackageJson)
@@ -18,6 +18,10 @@ object PropertyUtils {
const val HERMES_ENABLED = "hermesEnabled"
const val SCOPED_HERMES_ENABLED = "react.hermesEnabled"
/** Public property that toggles Hermes V1 */
const val HERMES_V1_ENABLED = "hermesV1Enabled"
const val SCOPED_HERMES_V1_ENABLED = "react.hermesV1Enabled"
/** Public property that toggles edge-to-edge */
const val EDGE_TO_EDGE_ENABLED = "edgeToEdgeEnabled"
const val SCOPED_EDGE_TO_EDGE_ENABLED = "react.edgeToEdgeEnabled"
@@ -68,9 +72,13 @@ object PropertyUtils {
const val INTERNAL_USE_HERMES_NIGHTLY = "react.internal.useHermesNightly"
/** Internal property used to override the publishing group for the React Native artifacts. */
const val INTERNAL_PUBLISHING_GROUP = "react.internal.publishingGroup"
const val DEFAULT_INTERNAL_PUBLISHING_GROUP = "com.facebook.react"
const val INTERNAL_REACT_PUBLISHING_GROUP = "react.internal.publishingGroup"
const val INTERNAL_HERMES_PUBLISHING_GROUP = "react.internal.hermesPublishingGroup"
const val DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP = "com.facebook.react"
const val DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP = "com.facebook.hermes"
/** Internal property used to control the version name of React Native */
const val INTERNAL_VERSION_NAME = "VERSION_NAME"
/** Internal property used to control the version name of Hermes Engine */
const val INTERNAL_HERMES_VERSION_NAME = "HERMES_VERSION_NAME"
}
@@ -271,11 +271,13 @@ class DependencyUtilsTest {
.isEqualTo(2)
}
// TODO: T236767053
@Test
fun configureDependencies_withEmptyVersion_doesNothing() {
val project = createProject()
configureDependencies(project, "")
configureDependencies(project, DependencyUtils.Coordinates("", ""))
assertThat(project.configurations.first().resolutionStrategy.forcedModules.isEmpty()).isTrue()
}
@@ -284,7 +286,7 @@ class DependencyUtilsTest {
fun configureDependencies_withVersionString_appliesResolutionStrategy() {
val project = createProject()
configureDependencies(project, "1.2.3")
configureDependencies(project, DependencyUtils.Coordinates("1.2.3", "1.2.3"))
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
@@ -301,7 +303,7 @@ class DependencyUtilsTest {
appProject.plugins.apply("com.android.application")
libProject.plugins.apply("com.android.library")
configureDependencies(appProject, "1.2.3")
configureDependencies(appProject, DependencyUtils.Coordinates("1.2.3", "1.2.3"))
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
@@ -323,7 +325,10 @@ class DependencyUtilsTest {
appProject.plugins.apply("com.android.application")
libProject.plugins.apply("com.android.library")
configureDependencies(appProject, "1.2.3", "io.github.test")
configureDependencies(
appProject,
DependencyUtils.Coordinates("1.2.3", "1.2.3", "io.github.test"),
)
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
@@ -339,7 +344,8 @@ class DependencyUtilsTest {
@Test
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly() {
val dependencySubstitutions = getDependencySubstitutions("0.42.0")
val dependencySubstitutions =
getDependencySubstitutions(DependencyUtils.Coordinates("0.42.0", "0.42.0"))
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
assertThat("com.facebook.react:react-android:0.42.0")
@@ -359,7 +365,10 @@ class DependencyUtilsTest {
@Test
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly() {
val dependencySubstitutions = getDependencySubstitutions("0.42.0", "io.github.test")
val dependencySubstitutions =
getDependencySubstitutions(
DependencyUtils.Coordinates("0.42.0", "0.42.0", "io.github.test")
)
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[0].second)
@@ -396,7 +405,7 @@ class DependencyUtilsTest {
)
}
val versionString = readVersionAndGroupStrings(propertiesFile).first
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
assertThat(versionString).isEqualTo("1000.0.0")
}
@@ -414,7 +423,7 @@ class DependencyUtilsTest {
)
}
val versionString = readVersionAndGroupStrings(propertiesFile).first
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
assertThat(versionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
}
@@ -431,7 +440,7 @@ class DependencyUtilsTest {
)
}
val versionString = readVersionAndGroupStrings(propertiesFile).first
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
assertThat(versionString).isEqualTo("")
}
@@ -448,7 +457,7 @@ class DependencyUtilsTest {
)
}
val versionString = readVersionAndGroupStrings(propertiesFile).first
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
assertThat(versionString).isEqualTo("")
}
@@ -465,7 +474,7 @@ class DependencyUtilsTest {
)
}
val groupString = readVersionAndGroupStrings(propertiesFile).second
val groupString = readVersionAndGroupStrings(propertiesFile).reactGroupString
assertThat(groupString).isEqualTo("io.github.test")
}
@@ -482,7 +491,7 @@ class DependencyUtilsTest {
)
}
val groupString = readVersionAndGroupStrings(propertiesFile).second
val groupString = readVersionAndGroupStrings(propertiesFile).reactGroupString
assertThat(groupString).isEqualTo("com.facebook.react")
}
@@ -162,6 +162,16 @@ class PathUtilsTest {
assertThat(detectOSAwareHermesCommand(tempFolder.root, "")).isEqualTo(expected.toString())
}
@Test
@WithOs(OS.MAC)
fun detectOSAwareHermesCommand_withHermesV1Enabled() {
tempFolder.newFolder("node_modules/hermes-compiler/osx-bin/")
val expected = tempFolder.newFile("node_modules/hermes-compiler/osx-bin//hermesc")
assertThat(detectOSAwareHermesCommand(tempFolder.root, "", hermesV1Enabled = true))
.isEqualTo(expected.toString())
}
@Test(expected = IllegalStateException::class)
@WithOs(OS.MAC)
fun detectOSAwareHermesCommand_failsIfNotFound() {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,8 +26,8 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.82.0-main",
"@react-native/metro-babel-transformer": "0.82.0-main",
"@react-native/js-polyfills": "0.82.0-rc.1",
"@react-native/metro-babel-transformer": "0.82.0-rc.1",
"metro-config": "^0.83.1",
"metro-runtime": "^0.83.1"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/new-app-screen",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "NewAppScreen component for React Native",
"keywords": [
"react-native"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Babel preset for React Native applications",
"repository": {
"type": "git",
@@ -66,7 +66,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.82.0-main",
"@react-native/babel-plugin-codegen": "0.82.0-rc.1",
"babel-plugin-syntax-hermes-parser": "0.32.0",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Babel transformer for React Native applications.",
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
],
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.82.0-main",
"@react-native/babel-preset": "0.82.0-rc.1",
"hermes-parser": "0.32.0",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/compatibility-check",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "Check a React Native app's boundary between JS and Native for incompatibilities",
"license": "MIT",
"repository": {
@@ -29,7 +29,7 @@
"dist"
],
"dependencies": {
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.82.0-rc.1"
},
"devDependencies": {
"flow-remove-types": "^2.237.2",
@@ -1,6 +1,6 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.82.0-main",
"version": "0.82.0-rc.1",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
@@ -21,7 +21,7 @@
},
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.82.0-rc.1"
},
"peerDependencies": {
"@types/react": "^19.1.0",
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @deprecated
*/
'use strict';
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @deprecated
*/
'use strict';
+3 -3
View File
@@ -26,10 +26,10 @@
* ```
*/
export default class ReactNativeVersion {
static major: number = 1000;
static minor: number = 0;
static major: number = 0;
static minor: number = 82;
static patch: number = 0;
static prerelease: string | null = null;
static prerelease: string | null = 'rc.1';
static getVersionString(): string {
return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
@@ -6,6 +6,7 @@
*
* @flow
* @format
* @deprecated
*/
import NativeTiming from './NativeTiming';
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @deprecated
*/
export * from '../../../src/private/specs_DEPRECATED/modules/NativeTiming';
@@ -6,6 +6,7 @@
*
* @flow
* @format
* @deprecated
*/
'use strict';
+3 -5
View File
@@ -4,19 +4,17 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
*/
import setUpPerformanceModern from '../../src/private/setup/setUpPerformanceModern';
import NativePerformance from '../../src/private/webapis/performance/specs/NativePerformance';
// In case if the native implementation of the Performance API is available, use it,
// otherwise fall back to the legacy/default one, which only defines 'Performance.now()'
if (NativePerformance) {
const Performance =
require('../../src/private/webapis/performance/Performance').default;
// $FlowExpectedError[cannot-write]
global.performance = new Performance();
setUpPerformanceModern();
} else {
if (!global.performance) {
// $FlowExpectedError[cannot-write]
@@ -21,10 +21,10 @@ NSDictionary* RCTGetReactNativeVersion(void)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(void){
__rnVersion = @{
RCTVersionMajor: @(1000),
RCTVersionMinor: @(0),
RCTVersionMajor: @(0),
RCTVersionMinor: @(82),
RCTVersionPatch: @(0),
RCTVersionPrerelease: [NSNull null],
RCTVersionPrerelease: @"rc.1",
};
});
return __rnVersion;
@@ -20,7 +20,12 @@
- (UIWindow *)alertWindow
{
if (_alertWindow == nil) {
_alertWindow = [[UIWindow alloc] initWithWindowScene:RCTKeyWindow().windowScene];
UIWindowScene *scene = RCTKeyWindow().windowScene;
if (scene != nil) {
_alertWindow = [[UIWindow alloc] initWithWindowScene:scene];
} else {
_alertWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
}
if (_alertWindow) {
_alertWindow.rootViewController = [UIViewController new];
@@ -39,6 +39,9 @@ val downloadsDir =
val thirdPartyNdkDir = File("$buildDir/third-party-ndk")
val reactNativeRootDir = projectDir.parent
val hermesV1Enabled =
rootProject.extensions.getByType(PrivateReactExtension::class.java).hermesV1Enabled.get()
// We put the publishing version from gradle.properties inside ext. so other
// subprojects can access it as well.
extra["publishing_version"] = project.findProperty("VERSION_NAME")?.toString()!!
@@ -565,6 +568,10 @@ android {
"-DCMAKE_POLICY_DEFAULT_CMP0069=NEW",
)
if (hermesV1Enabled) {
arguments("-DHERMES_V1_ENABLED=1")
}
targets(
"reactnative",
"jsi",
@@ -1,5 +1,7 @@
VERSION_NAME=1000.0.0
VERSION_NAME=0.82.0-rc.1
HERMES_VERSION_NAME=1000.0.0
react.internal.publishingGroup=com.facebook.react
react.internal.hermesPublishingGroup=com.facebook.hermes
android.useAndroidX=true
@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import com.facebook.react.internal.PrivateReactExtension
import com.facebook.react.tasks.internal.*
import de.undercouch.gradle.tasks.download.Download
import org.apache.tools.ant.taskdefs.condition.Os
@@ -50,6 +51,8 @@ fun getSDKManagerPath(): String {
}
}
val hermesV1Enabled =
rootProject.extensions.getByType(PrivateReactExtension::class.java).hermesV1Enabled.get()
val reactNativeRootDir = project(":packages:react-native:ReactAndroid").projectDir.parent
val customDownloadDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR")
val downloadsDir =
@@ -79,12 +82,21 @@ val hermesBuildOutputFileTree =
fileTree(hermesBuildDir.toString())
.include("**/*.cmake", "**/*.marks", "**/compiler_depends.ts", "**/Makefile", "**/link.txt")
var hermesVersion = "main"
val hermesVersionFile = File(reactNativeRootDir, "sdks/.hermesversion")
val hermesVersionProvider: Provider<String> =
providers.provider {
var hermesVersion = if (hermesV1Enabled) "250829098.0.0-stable" else "main"
val hermesVersionFile =
File(
reactNativeRootDir,
if (hermesV1Enabled) "sdks/.hermesv1version" else "sdks/.hermesversion",
)
if (hermesVersionFile.exists()) {
hermesVersion = hermesVersionFile.readText()
}
if (hermesVersionFile.exists()) {
hermesVersion = hermesVersionFile.readText()
}
hermesVersion
}
val ndkBuildJobs = Runtime.getRuntime().availableProcessors().toString()
val prefabHeadersDir = File("$buildDir/prefab-headers")
@@ -95,7 +107,11 @@ val jsiDir = File(reactNativeRootDir, "ReactCommon/jsi")
val downloadHermesDest = File(downloadsDir, "hermes.tar.gz")
val downloadHermes by
tasks.registering(Download::class) {
src("https://github.com/facebook/hermes/tarball/${hermesVersion}")
src(
providers.provider {
"https://github.com/facebook/hermes/tarball/${hermesVersionProvider.get()}"
}
)
onlyIfModified(true)
overwrite(true)
quiet(true)
@@ -151,6 +167,7 @@ val configureBuildForHermes by
"-B",
hermesBuildDir.toString(),
"-DJSI_DIR=" + jsiDir.absolutePath,
"-DCMAKE_BUILD_TYPE=Release",
)
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
cmakeCommandLine = cmakeCommandLine + "-GNMake Makefiles"
@@ -295,7 +312,11 @@ android {
// Therefore we're passing as build type Release, to provide a faster build.
// This has the (unlucky) side effect of letting AGP call the build
// tasks `configureCMakeRelease` while is actually building the debug flavor.
arguments("-DCMAKE_BUILD_TYPE=Release")
arguments(
"-DCMAKE_BUILD_TYPE=Release",
// For debug builds, explicitly enable the Hermes Debugger.
"-DHERMES_ENABLE_DEBUGGER=True",
)
}
}
}
@@ -1,15 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated by scripts/releases/set-version.js
*/
package com.facebook.react.modules.systeminfo
public object ReactNativeVersion {
@JvmField
public val VERSION: Map<String, Any?> =
mapOf("major" to 1000, "minor" to 0, "patch" to 0, "prerelease" to null)
public val VERSION: Map<String, Any?> = mapOf(
"major" to 0,
"minor" to 82,
"patch" to 0,
"prerelease" to "rc.1"
)
}
@@ -22,6 +22,8 @@ file(TO_CMAKE_PATH "${REACT_ANDROID_DIR}" REACT_ANDROID_DIR)
file(TO_CMAKE_PATH "${REACT_BUILD_DIR}" REACT_BUILD_DIR)
file(TO_CMAKE_PATH "${REACT_COMMON_DIR}" REACT_COMMON_DIR)
set(HERMES_V1_ENABLED OFF CACHE BOOL "Build with support for Hermes v1")
# If you have ccache installed, we're going to honor it.
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
@@ -27,4 +27,8 @@ target_link_libraries(
target_compile_reactnative_options(hermes_executor PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(hermes_executor PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(hermes_executor PRIVATE -DHERMES_V1_ENABLED=1)
endif()
endif()
@@ -29,4 +29,8 @@ target_link_libraries(hermesinstancejni
target_compile_reactnative_options(hermesinstancejni PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(hermesinstancejni PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(hermesinstancejni PRIVATE -DHERMES_V1_ENABLED=1)
endif()
endif ()
@@ -19,6 +19,10 @@ add_library(rninstance
target_compile_reactnative_options(rninstance PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(rninstance PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(rninstance PRIVATE -DHERMES_V1_ENABLED=1)
endif()
endif ()
target_merge_so(rninstance)
@@ -12,17 +12,17 @@
#include <cstdint>
#include <string_view>
#define REACT_NATIVE_VERSION_MAJOR 1000
#define REACT_NATIVE_VERSION_MINOR 0
#define REACT_NATIVE_VERSION_MAJOR 0
#define REACT_NATIVE_VERSION_MINOR 82
#define REACT_NATIVE_VERSION_PATCH 0
namespace facebook::react {
constexpr struct {
int32_t Major = 1000;
int32_t Minor = 0;
int32_t Major = 0;
int32_t Minor = 82;
int32_t Patch = 0;
std::string_view Prerelease = "";
std::string_view Prerelease = "rc.1";
} ReactNativeVersion;
} // namespace facebook::react
@@ -32,6 +32,10 @@ if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
PRIVATE
-DHERMES_ENABLE_DEBUGGER=1
)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(hermes_executor_common PRIVATE -DHERMES_V1_ENABLED=1)
endif()
else()
target_compile_options(
hermes_executor_common
@@ -14,8 +14,11 @@
#include <jsinspector-modern/InspectorFlags.h>
#include <hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.h>
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
#include <hermes/inspector-modern/chrome/Registration.h>
#include <hermes/inspector/RuntimeAdapter.h>
#endif
using namespace facebook::hermes;
using namespace facebook::jsi;
@@ -24,7 +27,7 @@ namespace facebook::react {
namespace {
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
class HermesExecutorRuntimeAdapter
: public facebook::hermes::inspector_modern::RuntimeAdapter {
@@ -59,7 +62,7 @@ class HermesExecutorRuntimeAdapter
std::shared_ptr<MessageQueueThread> thread_;
};
#endif // HERMES_ENABLE_DEBUGGER
#endif // defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
struct ReentrancyCheck {
// This is effectively a very subtle and complex assert, so only
@@ -144,7 +147,7 @@ class DecoratedRuntime : public jsi::WithRuntimeDecorator<ReentrancyCheck> {
const std::string& debuggerName)
: jsi::WithRuntimeDecorator<ReentrancyCheck>(*runtime, reentrancyCheck_),
runtime_(std::move(runtime)) {
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
enableDebugger_ = enableDebugger;
if (enableDebugger_) {
std::shared_ptr<HermesRuntime> rt(runtime_, &hermesRuntime);
@@ -159,7 +162,7 @@ class DecoratedRuntime : public jsi::WithRuntimeDecorator<ReentrancyCheck> {
}
~DecoratedRuntime() {
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
if (enableDebugger_) {
facebook::hermes::inspector_modern::chrome::disableDebugging(debugToken_);
}
@@ -176,7 +179,7 @@ class DecoratedRuntime : public jsi::WithRuntimeDecorator<ReentrancyCheck> {
std::shared_ptr<Runtime> runtime_;
ReentrancyCheck reentrancyCheck_;
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
bool enableDebugger_;
facebook::hermes::inspector_modern::chrome::DebugSessionToken debugToken_;
#endif // HERMES_ENABLE_DEBUGGER
@@ -23,6 +23,10 @@ if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
PRIVATE
-DHERMES_ENABLE_DEBUGGER=1
)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(hermes_inspector_modern PRIVATE -DHERMES_V1_ENABLED=1)
endif()
endif()
target_include_directories(hermes_inspector_modern PUBLIC ${REACT_COMMON_DIR})
@@ -7,7 +7,7 @@
#include "ConnectionDemux.h"
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/CDPHandler.h>
@@ -139,4 +139,4 @@ void ConnectionDemux::removePage(int pageId) {
} // namespace facebook::hermes::inspector_modern::chrome
#endif // HERMES_ENABLE_DEBUGGER
#endif // defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
@@ -7,7 +7,7 @@
#pragma once
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
#include <memory>
#include <mutex>
@@ -59,4 +59,4 @@ class ConnectionDemux {
} // namespace facebook::hermes::inspector_modern::chrome
#endif // HERMES_ENABLE_DEBUGGER
#endif // defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
@@ -8,7 +8,7 @@
#include "Registration.h"
#include "ConnectionDemux.h"
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
namespace facebook::hermes::inspector_modern::chrome {
@@ -34,4 +34,4 @@ void disableDebugging(DebugSessionToken session) {
} // namespace facebook::hermes::inspector_modern::chrome
#endif // HERMES_ENABLE_DEBUGGER
#endif // defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
@@ -7,7 +7,7 @@
#pragma once
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
#include <memory>
#include <string>
@@ -38,4 +38,4 @@ extern void disableDebugging(DebugSessionToken session);
} // namespace facebook::hermes::inspector_modern::chrome
#endif // HERMES_ENABLE_DEBUGGER
#endif // defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
@@ -54,6 +54,8 @@ Pod::Spec.new do |s|
add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork')
add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing')
s.dependency "React-perflogger", version
add_dependency(s, "React-oscompat")
if use_hermes()
s.dependency "hermes-engine"
end
@@ -18,6 +18,10 @@ add_library(bridgeless
target_compile_reactnative_options(bridgeless PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(bridgeless PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(bridgeless PRIVATE -DHERMES_V1_ENABLED=1)
endif()
endif ()
target_include_directories(bridgeless PUBLIC .)
@@ -35,4 +35,8 @@ if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
PRIVATE
-DHERMES_ENABLE_DEBUGGER=1
)
if (DEFINED HERMES_V1_ENABLED)
target_compile_options(bridgelesshermes PRIVATE -DHERMES_V1_ENABLED=1)
endif()
endif()
@@ -14,7 +14,11 @@
#ifdef HERMES_ENABLE_DEBUGGER
#include <hermes/inspector-modern/chrome/Registration.h>
#ifndef HERMES_V1_ENABLED
#include <hermes/inspector/RuntimeAdapter.h>
#endif
#include <jsi/decorator.h>
#endif
@@ -23,7 +27,7 @@ using namespace facebook::jsi;
namespace facebook::react {
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
// Wrapper that strongly retains the HermesRuntime for on device debugging.
//
@@ -90,7 +94,7 @@ class DecoratedRuntime : public jsi::RuntimeDecorator<jsi::Runtime> {
inspector_modern::chrome::DebugSessionToken debugToken_;
};
#endif
#endif // defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
class HermesJSRuntime : public JSRuntime {
public:
@@ -157,7 +161,7 @@ std::unique_ptr<JSRuntime> HermesInstance::createJSRuntime(
.getPropertyAsObject(*hermesRuntime, "prototype");
errorPrototype.setProperty(*hermesRuntime, "jsEngine", "hermes");
#ifdef HERMES_ENABLE_DEBUGGER
#if defined(HERMES_ENABLE_DEBUGGER) && !defined(HERMES_V1_ENABLED)
auto& inspectorFlags = jsinspector_modern::InspectorFlags::getInstance();
if (!inspectorFlags.getFuseboxEnabled()) {
std::unique_ptr<DecoratedRuntime> decoratedRuntime =
+157
View File
@@ -88,12 +88,169 @@ declare var navigator: Navigator;
// https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
declare type DOMHighResTimeStamp = number;
type PerformanceEntryFilterOptions = {
entryType: string,
name: string,
...
};
// https://www.w3.org/TR/performance-timeline-2/
declare class PerformanceEntry {
duration: DOMHighResTimeStamp;
entryType: string;
name: string;
startTime: DOMHighResTimeStamp;
toJSON(): string;
}
// https://w3c.github.io/user-timing/#performancemark
declare class PerformanceMark extends PerformanceEntry {
constructor(name: string, markOptions?: PerformanceMarkOptions): void;
+detail: mixed;
}
// https://w3c.github.io/user-timing/#performancemeasure
declare class PerformanceMeasure extends PerformanceEntry {
+detail: mixed;
}
// https://w3c.github.io/server-timing/#the-performanceservertiming-interface
declare class PerformanceServerTiming {
description: string;
duration: DOMHighResTimeStamp;
name: string;
toJSON(): string;
}
// https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming
// https://w3c.github.io/server-timing/#extension-to-the-performanceresourcetiming-interface
declare class PerformanceResourceTiming extends PerformanceEntry {
connectEnd: number;
connectStart: number;
decodedBodySize: number;
domainLookupEnd: number;
domainLookupStart: number;
encodedBodySize: number;
fetchStart: number;
initiatorType: string;
nextHopProtocol: string;
redirectEnd: number;
redirectStart: number;
requestStart: number;
responseEnd: number;
responseStart: number;
secureConnectionStart: number;
serverTiming: Array<PerformanceServerTiming>;
transferSize: number;
workerStart: number;
}
// https://w3c.github.io/event-timing/#sec-performance-event-timing
declare class PerformanceEventTiming extends PerformanceEntry {
cancelable: boolean;
interactionId: number;
processingEnd: number;
processingStart: number;
target: ?Node;
}
// https://w3c.github.io/longtasks/#taskattributiontiming
declare class TaskAttributionTiming extends PerformanceEntry {
containerId: string;
containerName: string;
containerSrc: string;
containerType: string;
}
// https://w3c.github.io/longtasks/#sec-PerformanceLongTaskTiming
declare class PerformanceLongTaskTiming extends PerformanceEntry {
attribution: $ReadOnlyArray<TaskAttributionTiming>;
}
// https://www.w3.org/TR/user-timing/#extensions-performance-interface
declare type PerformanceMarkOptions = {
detail?: mixed,
startTime?: number,
};
declare type PerformanceMeasureOptions = {
detail?: mixed,
duration?: number,
end?: number | string,
start?: number | string,
};
type EventCountsForEachCallbackType =
| (() => void)
| ((value: number) => void)
| ((value: number, key: string) => void)
| ((value: number, key: string, map: Map<string, number>) => void);
// https://www.w3.org/TR/event-timing/#eventcounts
declare interface EventCounts {
entries(): Iterator<[string, number]>;
forEach(callback: EventCountsForEachCallbackType): void;
get(key: string): ?number;
has(key: string): boolean;
keys(): Iterator<string>;
size: number;
values(): Iterator<number>;
}
declare class Performance {
clearMarks(name?: string): void;
clearMeasures(name?: string): void;
eventCounts: EventCounts;
getEntries: (
options?: PerformanceEntryFilterOptions,
) => Array<PerformanceEntry>;
getEntriesByName: (name: string, type?: string) => Array<PerformanceEntry>;
getEntriesByType: (type: string) => Array<PerformanceEntry>;
mark(name: string, options?: PerformanceMarkOptions): PerformanceMark;
measure(
name: string,
startMarkOrOptions?: string | PerformanceMeasureOptions,
endMark?: string,
): PerformanceMeasure;
now: () => DOMHighResTimeStamp;
toJSON(): string;
}
declare var performance: Performance;
type PerformanceEntryList = Array<PerformanceEntry>;
declare interface PerformanceObserverEntryList {
getEntries(): PerformanceEntryList;
getEntriesByName(name: string, type: ?string): PerformanceEntryList;
getEntriesByType(type: string): PerformanceEntryList;
}
type PerformanceObserverInit = {
buffered?: boolean,
entryTypes?: Array<string>,
type?: string,
...
};
declare class PerformanceObserver {
constructor(
callback: (
entries: PerformanceObserverEntryList,
observer: PerformanceObserver,
) => mixed,
): void;
disconnect(): void;
observe(options: ?PerformanceObserverInit): void;
static supportedEntryTypes: Array<string>;
takeRecords(): PerformanceEntryList;
}
type FormDataEntryValue = string | File;
declare class FormData {
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.82.0-rc.1",
"description": "A framework for building native apps using React",
"license": "MIT",
"repository": {
@@ -162,13 +162,13 @@
},
"dependencies": {
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/assets-registry": "0.82.0-main",
"@react-native/codegen": "0.82.0-main",
"@react-native/community-cli-plugin": "0.82.0-main",
"@react-native/gradle-plugin": "0.82.0-main",
"@react-native/js-polyfills": "0.82.0-main",
"@react-native/normalize-colors": "0.82.0-main",
"@react-native/virtualized-lists": "0.82.0-main",
"@react-native/assets-registry": "0.82.0-rc.1",
"@react-native/codegen": "0.82.0-rc.1",
"@react-native/community-cli-plugin": "0.82.0-rc.1",
"@react-native/gradle-plugin": "0.82.0-rc.1",
"@react-native/js-polyfills": "0.82.0-rc.1",
"@react-native/normalize-colors": "0.82.0-rc.1",
"@react-native/virtualized-lists": "0.82.0-rc.1",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@@ -40,6 +40,12 @@ def list_native_modules!(config_command)
packages = config["dependencies"]
ios_project_root = Pathname.new(config["project"]["ios"]["sourceDir"])
react_native_path = Pathname.new(config["reactNativePath"])
codegen_output_path = ios_project_root.join("build/generated/autolinking/autolinking.json")
# Write autolinking react-native-config output to codegen folder
FileUtils.mkdir_p(File.dirname(codegen_output_path))
File.write(codegen_output_path, json)
found_pods = []
packages.each do |package_name, package|
@@ -87,7 +87,7 @@ class CodegenUtils
codegen_path = file_manager.join(ios_folder, codegen_dir)
return if !dir_manager.exist?(codegen_path)
FileUtils.rm_rf(dir_manager.glob("#{codegen_path}/*"))
FileUtils.rm_rf("#{codegen_path}")
base_provider_path = file_manager.join(rn_path, 'React', 'Fabric', 'RCTThirdPartyFabricComponentsProvider')
FileUtils.rm_rf("#{base_provider_path}.h")
FileUtils.rm_rf("#{base_provider_path}.mm")
@@ -13,7 +13,8 @@ def setup_hermes!(react_native_path: "../node_modules/react-native")
react_native_dir = Pod::Config.instance.installation_root.join(react_native_path)
# This `:tag => hermestag` below is only to tell CocoaPods to update hermes-engine when React Native version changes.
# We have custom logic to compute the source for hermes-engine. See sdks/hermes-engine/*
hermestag_file = File.join(react_native_dir, "sdks", ".hermesversion")
hermestag_file_name = ENV['RCT_HERMES_V1_ENABLED'] == "1" ? ".hermesv1version" : ".hermesversion"
hermestag_file = File.join(react_native_dir, "sdks", hermestag_file_name)
hermestag = File.exist?(hermestag_file) ? File.read(hermestag_file).strip : ''
pod 'hermes-engine', :podspec => "#{react_native_path}/sdks/hermes-engine/hermes-engine.podspec", :tag => hermestag
pod 'React-hermes', :path => "#{react_native_path}/ReactCommon/hermes"
@@ -45,9 +45,15 @@ class ReactNativePodsUtils
end
def self.set_gcc_preprocessor_definition_for_React_hermes(installer)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-hermes", :debug)
if ENV['RCT_HERMES_V1_ENABLED'] == "1"
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1 HERMES_V1_ENABLED=1", "React-hermes", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1 HERMES_V1_ENABLED=1", "React-RuntimeHermes", :debug)
else
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-hermes", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-RuntimeHermes", :debug)
end
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "hermes-engine", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-RuntimeHermes", :debug)
end
def self.set_gcc_preprocessor_definition_for_debugger(installer)
@@ -361,7 +361,7 @@ exports[`execute test-app "ReactAppDependencyProvider.podspec" should match snap
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
version = \\"1000.0.0\\"
version = \\"0.82.0-rc.1\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
@@ -399,7 +399,7 @@ exports[`execute test-app "ReactCodegen.podspec" should match snapshot 1`] = `
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
version = \\"1000.0.0\\"
version = \\"0.82.0-rc.1\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
@@ -840,7 +840,7 @@ exports[`execute test-app-legacy "ReactAppDependencyProvider.podspec" should mat
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
version = \\"1000.0.0\\"
version = \\"0.82.0-rc.1\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
@@ -878,7 +878,7 @@ exports[`execute test-app-legacy "ReactCodegen.podspec" should match snapshot 1`
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
version = \\"1000.0.0\\"
version = \\"0.82.0-rc.1\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
@@ -984,4 +984,4 @@ SCRIPT
end
"
`;
`;
@@ -86,10 +86,14 @@ function execute(
buildCodegenIfNeeded();
}
const reactNativeConfig = readReactNativeConfig(projectRoot);
const reactNativeConfig = readReactNativeConfig(
projectRoot,
baseOutputPath,
);
const codegenEnabledLibraries = findCodegenEnabledLibraries(
pkgJson,
projectRoot,
baseOutputPath,
reactNativeConfig,
);
@@ -97,15 +97,40 @@ function cleanupEmptyFilesAndFolders(filepath /*: string */) {
}
}
function readReactNativeConfig(projectRoot /*: string */) /*: $FlowFixMe */ {
const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js');
function readGeneratedAutolinkingOutput(
baseOutputPath /*: string */,
) /*: $FlowFixMe */ {
// NOTE: Generated by scripts/cocoapods/autolinking.rb in list_native_modules (called by use_native_modules)
const autolinkingGeneratedPath = path.resolve(
baseOutputPath,
'build/generated/autolinking/autolinking.json',
);
if (fs.existsSync(autolinkingGeneratedPath)) {
// $FlowFixMe[unsupported-syntax]
return require(autolinkingGeneratedPath);
} else {
codegenLog(
`Could not find generated autolinking output at: ${autolinkingGeneratedPath}`,
);
return null;
}
}
if (!fs.existsSync(rnConfigFilePath)) {
function readReactNativeConfig(
projectRoot /*: string */,
baseOutputPath /*: string */,
) /*: $FlowFixMe */ {
const autolinkingOutput = readGeneratedAutolinkingOutput(baseOutputPath);
const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js');
if (autolinkingOutput) {
return autolinkingOutput;
} else if (fs.existsSync(rnConfigFilePath)) {
// $FlowFixMe[unsupported-syntax]
return require(rnConfigFilePath);
} else {
codegenLog(`Could not find React Native config at: ${rnConfigFilePath}`);
return {};
}
// $FlowFixMe[unsupported-syntax]
return require(rnConfigFilePath);
}
/**
@@ -114,17 +139,23 @@ function readReactNativeConfig(projectRoot /*: string */) /*: $FlowFixMe */ {
function findCodegenEnabledLibraries(
pkgJson /*: $FlowFixMe */,
projectRoot /*: string */,
baseOutputPath /*: string */,
reactNativeConfig /*: $FlowFixMe */,
) /*: Array<$FlowFixMe> */ {
const projectLibraries = findProjectRootLibraries(pkgJson, projectRoot);
if (pkgJsonIncludesGeneratedCode(pkgJson)) {
return projectLibraries;
} else {
return [
...projectLibraries,
...findExternalLibraries(pkgJson, projectRoot),
const libraries = [...projectLibraries];
// If we ran autolinking, we shouldn't try to run our own "autolinking-like"
// library discovery
if (!readGeneratedAutolinkingOutput(baseOutputPath)) {
libraries.push(...findExternalLibraries(pkgJson, projectRoot));
}
libraries.push(
...findLibrariesFromReactNativeConfig(projectRoot, reactNativeConfig),
];
);
return libraries;
}
}
+4 -2
View File
@@ -10,7 +10,7 @@
'use strict';
const {execSync} = require('child_process');
const {spawnSync} = require('child_process');
const fs = require('fs');
const yargs = require('yargs');
@@ -67,7 +67,9 @@ function replaceRNCoreConfiguration(
fs.mkdirSync(finalLocation, {recursive: true});
console.log('Extracting the tarball', tarballURLPath);
execSync(`tar -xf ${tarballURLPath} -C ${finalLocation}`);
spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], {
stdio: 'inherit',
});
}
function updateLastBuildConfiguration(configuration /*: string */) {
@@ -0,0 +1 @@
hermes-2025-09-01-RNv0.82.0-265ef62ff3eb7289d17e366664ac0da82303e101
@@ -20,6 +20,7 @@ end
# package.json
package = JSON.parse(File.read(File.join(react_native_path, "package.json")))
# TODO: T231755000 use the Hermes V1 version instead of React Native version
version = package['version']
source_type = hermes_source_type(version, react_native_path)
@@ -62,6 +63,21 @@ Pod::Spec.new do |spec|
ss.osx.vendored_frameworks = "destroot/Library/Frameworks/macosx/hermesvm.framework"
end
# When using the local prebuilt tarball, it should include hermesc compatible with the used VM.
# In other cases, when using Hermes V1, the prebuilt versioned binaries can be used.
# TODO: T236142916 hermesc should be consumed from NPM even when not using Hermes V1
if source_type != HermesEngineSourceType::LOCAL_PREBUILT_TARBALL && ENV['RCT_HERMES_V1_ENABLED'] == "1"
hermes_compiler_path = File.dirname(Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"hermes-compiler",
{paths: [process.argv[1]]}
)', __dir__]).strip
)
spec.user_target_xcconfig = {
'HERMES_CLI_PATH' => "#{hermes_compiler_path}/osx-bin/hermesc"
}
end
# Right now, even reinstalling pods with the PRODUCTION flag turned on, does not change the version of hermes that is downloaded
# To remove the PRODUCTION flag, we want to download the right version of hermes on the flight
@@ -100,24 +116,26 @@ Pod::Spec.new do |spec|
ss.header_dir = 'hermes/cdp'
end
spec.subspec 'inspector' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/inspector/*.h'
ss.header_dir = 'hermes/inspector'
end
spec.subspec 'inspector_chrome' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/inspector/chrome/*.h'
ss.header_dir = 'hermes/inspector/chrome'
end
spec.subspec 'Public' do |ss|
ss.source_files = ''
ss.public_header_files = 'public/hermes/Public/*.h'
ss.header_dir = 'hermes/Public'
end
if ENV['RCT_HERMES_V1_ENABLED'] != "1"
spec.subspec 'inspector' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/inspector/*.h'
ss.header_dir = 'hermes/inspector'
end
spec.subspec 'inspector_chrome' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/inspector/chrome/*.h'
ss.header_dir = 'hermes/inspector/chrome'
end
end
hermesc_path = "${PODS_ROOT}/hermes-engine/build_host_hermesc"
if ENV.has_key?('HERMES_OVERRIDE_HERMESC_PATH') && File.exist?(ENV['HERMES_OVERRIDE_HERMESC_PATH']) then
@@ -85,6 +85,10 @@ def hermes_commit_envvar_defined()
return ENV.has_key?('HERMES_COMMIT')
end
def hermes_v1_enabled()
return ENV['RCT_HERMES_V1_ENABLED'] == "1"
end
def force_build_from_tag(react_native_path)
return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && File.exist?(hermestag_file(react_native_path))
end
@@ -170,13 +174,19 @@ end
def podspec_source_build_from_github_tag(react_native_path)
tag = File.read(hermestag_file(react_native_path)).strip
hermes_log("Using tag difined in sdks/.hermesversion: #{tag}")
if hermes_v1_enabled()
hermes_log("Using tag defined in sdks/.hermesv1version: #{tag}")
else
hermes_log("Using tag defined in sdks/.hermesversion: #{tag}")
end
return {:git => HERMES_GITHUB_URL, :tag => tag}
end
def podspec_source_build_from_github_main()
hermes_log("Using the latest commit from main.")
return {:git => HERMES_GITHUB_URL, :commit => `git ls-remote #{HERMES_GITHUB_URL} main | cut -f 1`.strip}
branch = hermes_v1_enabled() ? "250829098.0.0-stable" : "main"
hermes_log("Using the latest commit from #{branch}.")
return {:git => HERMES_GITHUB_URL, :commit => `git ls-remote #{HERMES_GITHUB_URL} #{branch} | cut -f 1`.strip}
end
def podspec_source_download_prebuild_release_tarball(react_native_path, version)
@@ -200,7 +210,11 @@ def artifacts_dir()
end
def hermestag_file(react_native_path)
return File.join(react_native_path, "sdks", ".hermesversion")
if hermes_v1_enabled()
return File.join(react_native_path, "sdks", ".hermesv1version")
else
return File.join(react_native_path, "sdks", ".hermesversion")
end
end
def release_tarball_url(version, build_type)
@@ -210,10 +224,18 @@ def release_tarball_url(version, build_type)
ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ?
ENV['ENTERPRISE_REPOSITORY'] :
"https://repo1.maven.org/maven2"
namespace = "com/facebook/react"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.71.0/react-native-artifacts-0.71.0-hermes-ios-debug.tar.gz
return "#{maven_repo_url}/#{namespace}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-hermes-ios-#{build_type.to_s}.tar.gz"
if hermes_v1_enabled()
namespace = "com/facebook/hermes"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/0.14.0/hermes-ios-0.14.0-debug.tar.gz
return "#{maven_repo_url}/#{namespace}/hermes-ios/#{version}/hermes-ios-#{version}-#{build_type.to_s}.tar.gz"
else
namespace = "com/facebook/react"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.71.0/react-native-artifacts-0.71.0-hermes-ios-debug.tar.gz
return "#{maven_repo_url}/#{namespace}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-hermes-ios-#{build_type.to_s}.tar.gz"
end
end
def download_stable_hermes(react_native_path, version, configuration)
@@ -235,9 +257,18 @@ def download_hermes_tarball(react_native_path, tarball_url, version, configurati
end
def nightly_tarball_url(version)
# TODO: T231755027 update coordinates and versioning
artifact_coordinate = "react-native-artifacts"
artifact_name = "hermes-ios-debug.tar.gz"
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artifact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
namespace = "com/facebook/react"
if hermes_v1_enabled()
artifact_coordinate = "hermes-ios"
artifact_name = "hermes-ios-debug.tar.gz"
namespace = "com/facebook/hermes"
end
xml_url = "https://central.sonatype.com/repository/maven-snapshots/#{namespace}/#{artifact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
response = Net::HTTP.get_response(URI(xml_url))
if response.is_a?(Net::HTTPSuccess)
@@ -245,7 +276,7 @@ def nightly_tarball_url(version)
timestamp = xml.elements['metadata/versioning/snapshot/timestamp'].text
build_number = xml.elements['metadata/versioning/snapshot/buildNumber'].text
full_version = "#{version}-#{timestamp}-#{build_number}"
final_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artifact_coordinate}/#{version}-SNAPSHOT/#{artifact_coordinate}-#{full_version}-#{artifact_name}"
final_url = "https://central.sonatype.com/repository/maven-snapshots/#{namespace}/#{artifact_coordinate}/#{version}-SNAPSHOT/#{artifact_coordinate}-#{full_version}-#{artifact_name}"
return final_url
else
@@ -88,6 +88,11 @@ function configure_apple_framework {
xcode_15_flags="LINKER:-ld_classic"
fi
boost_context_flag=""
if [[ $1 == "catalyst" ]]; then
boost_context_flag="-DHERMES_ALLOW_BOOST_CONTEXT=0"
fi
pushd "$HERMES_PATH" > /dev/null || exit 1
cmake -S . -B "build_$1" \
-DHERMES_EXTRA_LINKER_FLAGS="$xcode_15_flags" \
@@ -107,7 +112,8 @@ function configure_apple_framework {
-DIMPORT_HOST_COMPILERS:PATH="$IMPORT_HOST_COMPILERS_PATH" \
-DJSI_DIR="$JSI_PATH" \
-DHERMES_RELEASE_VERSION="for RN $(get_release_version)" \
-DCMAKE_BUILD_TYPE="$cmake_build_type"
-DCMAKE_BUILD_TYPE="$cmake_build_type" \
$boost_context_flag
popd > /dev/null || exit 1
}
@@ -59,6 +59,11 @@ if [[ $xcode_major_version -ge 15 ]]; then
xcode_15_flags="LINKER:-ld_classic"
fi
boost_context_flag=""
if [[ $PLATFORM_NAME == "catalyst" ]]; then
boost_context_flag="-DHERMES_ALLOW_BOOST_CONTEXT=0"
fi
architectures=$( echo "$ARCHS" | tr " " ";" )
echo "Configure Apple framework"
@@ -83,7 +88,8 @@ echo "Configure Apple framework"
-DIMPORT_HOST_COMPILERS:PATH="${hermesc_path}" \
-DJSI_DIR="$jsi_path" \
-DHERMES_RELEASE_VERSION="for RN $release_version" \
-DCMAKE_BUILD_TYPE="$cmake_build_type"
-DCMAKE_BUILD_TYPE="$cmake_build_type" \
$boost_context_flag
echo "Build Apple framework"
@@ -96,6 +102,7 @@ echo "Copy Apple framework to destroot/Library/Frameworks"
platform_copy_destination=$(get_platform_copy_destination $PLATFORM_NAME)
mkdir -p "${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/${platform_copy_destination}"
cp -pfR \
"${PODS_ROOT}/hermes-engine/build/${PLATFORM_NAME}/lib/hermesvm.framework" \
"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/${platform_copy_destination}"
@@ -18,7 +18,7 @@ SDKROOT=$(xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
env -i \
PATH="$PATH" \
SDKROOT="$SDKROOT" \
"$CMAKE_BINARY" -S "${PODS_ROOT}/hermes-engine" -B "$hermesc_dir_path" -DJSI_DIR="$jsi_path"
"$CMAKE_BINARY" -S "${PODS_ROOT}/hermes-engine" -B "$hermesc_dir_path" -DJSI_DIR="$jsi_path" -DCMAKE_BUILD_TYPE=Release
env -i \
PATH="$PATH" \
@@ -10,7 +10,7 @@
'use strict';
const {execSync} = require('child_process');
const {spawnSync} = require('child_process');
const fs = require('fs');
const yargs = require('yargs');
@@ -62,7 +62,9 @@ function replaceHermesConfiguration(configuration, version, podsRoot) {
fs.mkdirSync(finalLocation, {recursive: true});
console.log('Extracting the tarball');
execSync(`tar -xf ${tarballURLPath} -C ${finalLocation}`);
spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], {
stdio: 'inherit',
});
}
function updateLastBuildConfiguration(configuration) {
@@ -29,3 +29,12 @@ include(":packages:react-native:ReactAndroid:hermes-engine")
project(":packages:react-native:ReactAndroid:hermes-engine").projectDir =
file("ReactAndroid/hermes-engine/")
// Since Gradle 9.0, all the projects in the path must have an existing folder.
// As we build :packages:react-native:ReactAndroid, we need to declare the folders
// for :packages and :packages:react-native as well as otherwise the build from
// source will fail with a missing folder exception.
project(":packages").projectDir = file("/tmp")
project(":packages:react-native").projectDir = file("/tmp")
@@ -12,13 +12,62 @@ import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions';
let initialized = false;
export default function setUpPerformanceObserver() {
export default function setUpPerformanceModern() {
if (initialized) {
return;
}
initialized = true;
const Performance = require('../webapis/performance/Performance').default;
// We don't use `polyfillGlobal` to define this lazily because the
// `performance` object is always accessed.
// $FlowExpectedError[cannot-write]
global.performance = new Performance();
polyfillGlobal(
'EventCounts',
() => require('../webapis/performance/EventTiming').EventCounts_public,
);
polyfillGlobal(
'Performance',
() => require('../webapis/performance/Performance').Performance_public,
);
polyfillGlobal(
'PerformanceEntry',
() =>
require('../webapis/performance/PerformanceEntry')
.PerformanceEntry_public,
);
polyfillGlobal(
'PerformanceEventTiming',
() =>
require('../webapis/performance/EventTiming')
.PerformanceEventTiming_public,
);
polyfillGlobal(
'PerformanceLongTaskTiming',
() =>
require('../webapis/performance/LongTasks')
.PerformanceLongTaskTiming_public,
);
polyfillGlobal(
'PerformanceMark',
() => require('../webapis/performance/UserTiming').PerformanceMark,
);
polyfillGlobal(
'PerformanceMeasure',
() =>
require('../webapis/performance/UserTiming').PerformanceMeasure_public,
);
polyfillGlobal(
'PerformanceObserver',
() =>
@@ -29,43 +78,19 @@ export default function setUpPerformanceObserver() {
'PerformanceObserverEntryList',
() =>
require('../webapis/performance/PerformanceObserver')
.PerformanceObserverEntryList,
);
polyfillGlobal(
'PerformanceEntry',
() => require('../webapis/performance/PerformanceEntry').PerformanceEntry,
);
polyfillGlobal(
'PerformanceMark',
() => require('../webapis/performance/UserTiming').PerformanceMark,
);
polyfillGlobal(
'PerformanceMeasure',
() => require('../webapis/performance/UserTiming').PerformanceMeasure,
);
polyfillGlobal(
'PerformanceEventTiming',
() => require('../webapis/performance/EventTiming').PerformanceEventTiming,
.PerformanceObserverEntryList_public,
);
polyfillGlobal(
'PerformanceResourceTiming',
() =>
require('../webapis/performance/ResourceTiming')
.PerformanceResourceTiming,
.PerformanceResourceTiming_public,
);
polyfillGlobal(
'TaskAttributionTiming',
() => require('../webapis/performance/LongTasks').TaskAttributionTiming,
);
polyfillGlobal(
'PerformanceLongTaskTiming',
() => require('../webapis/performance/LongTasks').PerformanceLongTaskTiming,
() =>
require('../webapis/performance/LongTasks').TaskAttributionTiming_public,
);
}
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @deprecated
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
@@ -9,9 +9,9 @@
*/
// flowlint unsafe-getters-setters:off
import type {
DOMHighResTimeStamp,
PerformanceEntryInit,
PerformanceEntryJSON,
} from './PerformanceEntry';
@@ -29,25 +29,20 @@ export type PerformanceEventTimingJSON = {
...
};
export interface PerformanceEventTimingInit extends PerformanceEntryInit {
+processingStart?: DOMHighResTimeStamp;
+processingEnd?: DOMHighResTimeStamp;
+interactionId?: number;
}
export class PerformanceEventTiming extends PerformanceEntry {
#processingStart: DOMHighResTimeStamp;
#processingEnd: DOMHighResTimeStamp;
#interactionId: number;
constructor(init: {
name: string,
startTime?: DOMHighResTimeStamp,
duration?: DOMHighResTimeStamp,
processingStart?: DOMHighResTimeStamp,
processingEnd?: DOMHighResTimeStamp,
interactionId?: number,
}) {
super({
name: init.name,
entryType: 'event',
startTime: init.startTime ?? 0,
duration: init.duration ?? 0,
});
constructor(init: PerformanceEventTimingInit) {
super('event', init);
this.#processingStart = init.processingStart ?? 0;
this.#processingEnd = init.processingEnd ?? 0;
this.#interactionId = init.interactionId ?? 0;
@@ -75,6 +70,18 @@ export class PerformanceEventTiming extends PerformanceEntry {
}
}
export const PerformanceEventTiming_public: typeof PerformanceEventTiming =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function PerformanceEventTiming() {
throw new TypeError(
"Failed to construct 'PerformanceEventTiming': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
PerformanceEventTiming_public.prototype = PerformanceEventTiming.prototype;
type EventCountsForEachCallbackType =
| (() => void)
| ((value: number) => void)
@@ -139,3 +146,15 @@ export class EventCounts {
return getCachedEventCounts().values();
}
}
export const EventCounts_public: typeof EventCounts =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function EventCounts() {
throw new TypeError(
"Failed to construct 'EventCounts': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
EventCounts_public.prototype = EventCounts.prototype;
@@ -9,8 +9,10 @@
*/
// flowlint unsafe-getters-setters:off
import type {PerformanceEntryJSON} from './PerformanceEntry';
import type {
PerformanceEntryInit,
PerformanceEntryJSON,
} from './PerformanceEntry';
import {PerformanceEntry} from './PerformanceEntry';
@@ -22,10 +24,28 @@ export type PerformanceLongTaskTimingJSON = {
export class TaskAttributionTiming extends PerformanceEntry {}
export const TaskAttributionTiming_public: typeof TaskAttributionTiming =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function TaskAttributionTiming() {
throw new TypeError(
"Failed to construct 'TaskAttributionTiming': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
TaskAttributionTiming_public.prototype = TaskAttributionTiming.prototype;
const EMPTY_ATTRIBUTION: $ReadOnlyArray<TaskAttributionTiming> =
Object.preventExtensions([]);
export interface PerformanceLongTaskTimingInit extends PerformanceEntryInit {}
export class PerformanceLongTaskTiming extends PerformanceEntry {
constructor(init: PerformanceEntryInit) {
super('longtask', init);
}
get attribution(): $ReadOnlyArray<TaskAttributionTiming> {
return EMPTY_ATTRIBUTION;
}
@@ -37,3 +57,16 @@ export class PerformanceLongTaskTiming extends PerformanceEntry {
};
}
}
export const PerformanceLongTaskTiming_public: typeof PerformanceLongTaskTiming =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function PerformanceLongTaskTiming() {
throw new TypeError(
"Failed to construct 'PerformanceLongTaskTiming': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
PerformanceLongTaskTiming_public.prototype =
PerformanceLongTaskTiming.prototype;
@@ -64,12 +64,13 @@ const cachedGetMarkTime = NativePerformance.getMarkTime;
const cachedNativeClearMarks = NativePerformance.clearMarks;
const cachedNativeClearMeasures = NativePerformance.clearMeasures;
const MARK_OPTIONS_REUSABLE_OBJECT: {...PerformanceMarkOptions} = {
const MARK_OPTIONS_REUSABLE_OBJECT: PerformanceMarkOptions = {
startTime: 0,
detail: undefined,
};
const MEASURE_OPTIONS_REUSABLE_OBJECT: {...PerformanceMeasureInit} = {
const MEASURE_OPTIONS_REUSABLE_OBJECT: PerformanceMeasureInit = {
name: '',
startTime: 0,
duration: 0,
detail: undefined,
@@ -189,7 +190,9 @@ export default class Performance {
resolvedDetail = structuredClone(detail);
}
// $FlowExpectedError[cannot-write]
MARK_OPTIONS_REUSABLE_OBJECT.startTime = resolvedStartTime;
// $FlowExpectedError[cannot-write]
MARK_OPTIONS_REUSABLE_OBJECT.detail = resolvedDetail;
const entry = new PerformanceMark(
@@ -367,14 +370,16 @@ export default class Performance {
}
}
// $FlowExpectedError[cannot-write]
MEASURE_OPTIONS_REUSABLE_OBJECT.name = resolvedMeasureName;
// $FlowExpectedError[cannot-write]
MEASURE_OPTIONS_REUSABLE_OBJECT.startTime = resolvedStartTime;
// $FlowExpectedError[cannot-write]
MEASURE_OPTIONS_REUSABLE_OBJECT.duration = resolvedDuration;
// $FlowExpectedError[cannot-write]
MEASURE_OPTIONS_REUSABLE_OBJECT.detail = resolvedDetail;
const entry = new PerformanceMeasure(
resolvedMeasureName,
MEASURE_OPTIONS_REUSABLE_OBJECT,
);
const entry = new PerformanceMeasure(MEASURE_OPTIONS_REUSABLE_OBJECT);
cachedReportMeasure(
resolvedMeasureName,
@@ -438,4 +443,16 @@ export default class Performance {
}
}
export const Performance_public: typeof Performance =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function Performance() {
throw new TypeError(
"Failed to construct 'Performance': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
Performance_public.prototype = Performance.prototype;
setPlatformObject(Performance);
@@ -28,24 +28,25 @@ export type PerformanceEntryJSON = {
...
};
export interface PerformanceEntryInit {
+name: string;
+startTime: DOMHighResTimeStamp;
+duration: DOMHighResTimeStamp;
}
export class PerformanceEntry {
// We don't use private fields because they're significantly slower to
// initialize on construction and to access.
// We also need these to be protected so they can be initialized in subclasses
// where we avoid calling `super()` for performance reasons.
__name: string;
__entryType: PerformanceEntryType;
__name: string;
__startTime: DOMHighResTimeStamp;
__duration: DOMHighResTimeStamp;
constructor(init: {
name: string,
entryType: PerformanceEntryType,
startTime: DOMHighResTimeStamp,
duration: DOMHighResTimeStamp,
}) {
constructor(entryType: PerformanceEntryType, init: PerformanceEntryInit) {
this.__entryType = entryType;
this.__name = init.name;
this.__entryType = init.entryType;
this.__startTime = init.startTime;
this.__duration = init.duration;
}
@@ -76,6 +77,18 @@ export class PerformanceEntry {
}
}
export const PerformanceEntry_public: typeof PerformanceEntry =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function PerformanceEntry() {
throw new TypeError(
"Failed to construct 'PerformanceEntry': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
PerformanceEntry_public.prototype = PerformanceEntry.prototype;
setPlatformObject(PerformanceEntry);
export type PerformanceEntryList = $ReadOnlyArray<PerformanceEntry>;
@@ -57,6 +57,19 @@ export class PerformanceObserverEntryList {
}
}
export const PerformanceObserverEntryList_public: typeof PerformanceObserverEntryList =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function PerformanceObserverEntryList() {
throw new TypeError(
"Failed to construct 'PerformanceObserverEntryList': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
PerformanceObserverEntryList_public.prototype =
PerformanceObserverEntryList.prototype;
export type PerformanceObserverCallbackOptions = {
droppedEntriesCount: number,
};
@@ -145,6 +158,22 @@ export class PerformanceObserver {
NativePerformance.disconnect(this.#nativeObserverHandle);
}
takeRecords(): PerformanceEntryList {
let entries: PerformanceEntryList = [];
if (this.#nativeObserverHandle != null) {
const rawEntries = NativePerformance.takeRecords(
this.#nativeObserverHandle,
true,
);
if (rawEntries && rawEntries.length > 0) {
entries = rawEntries.map(rawToPerformanceEntry);
}
}
return entries;
}
#createNativeObserver(): OpaqueNativeObserverHandle | null {
this.#calledAtLeastOnce = false;
@@ -154,7 +183,7 @@ export class PerformanceObserver {
observerHandle,
true, // sort records
);
if (!rawEntries) {
if (!rawEntries || rawEntries.length === 0) {
return;
}
@@ -29,6 +29,19 @@ export type PerformanceResourceTimingJSON = {
...
};
export interface PerformanceResourceTimingInit {
+name: string;
+startTime: DOMHighResTimeStamp;
+duration: DOMHighResTimeStamp;
+fetchStart: DOMHighResTimeStamp;
+requestStart: DOMHighResTimeStamp;
+connectStart: DOMHighResTimeStamp;
+connectEnd: DOMHighResTimeStamp;
+responseStart: DOMHighResTimeStamp;
+responseEnd: DOMHighResTimeStamp;
+responseStatus?: number;
}
export class PerformanceResourceTiming extends PerformanceEntry {
#fetchStart: DOMHighResTimeStamp;
#requestStart: DOMHighResTimeStamp;
@@ -38,24 +51,9 @@ export class PerformanceResourceTiming extends PerformanceEntry {
#responseEnd: DOMHighResTimeStamp;
#responseStatus: ?number;
constructor(init: {
name: string,
startTime: DOMHighResTimeStamp,
duration: DOMHighResTimeStamp,
fetchStart: DOMHighResTimeStamp,
requestStart: DOMHighResTimeStamp,
connectStart: DOMHighResTimeStamp,
connectEnd: DOMHighResTimeStamp,
responseStart: DOMHighResTimeStamp,
responseEnd: DOMHighResTimeStamp,
responseStatus?: number,
}) {
super({
name: init.name,
entryType: 'resource',
startTime: init.startTime,
duration: init.duration,
});
constructor(init: PerformanceResourceTimingInit) {
super('resource', init);
this.#fetchStart = init.fetchStart;
this.#requestStart = init.requestStart;
this.#connectStart = init.connectStart;
@@ -106,3 +104,16 @@ export class PerformanceResourceTiming extends PerformanceEntry {
};
}
}
export const PerformanceResourceTiming_public: typeof PerformanceResourceTiming =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function PerformanceResourceTiming() {
throw new TypeError(
"Failed to construct 'PerformanceResourceTiming': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
PerformanceResourceTiming_public.prototype =
PerformanceResourceTiming.prototype;
@@ -9,8 +9,10 @@
*/
// flowlint unsafe-getters-setters:off
import type {DOMHighResTimeStamp} from './PerformanceEntry';
import type {
DOMHighResTimeStamp,
PerformanceEntryInit,
} from './PerformanceEntry';
import type {
ExtensionMarkerPayload,
ExtensionTrackEntryPayload,
@@ -25,18 +27,16 @@ export type DetailType =
// but we'll use it as documentation for how to use the extensibility API.
| {devtools?: ExtensionMarkerPayload | ExtensionTrackEntryPayload, ...};
export type PerformanceMarkOptions = $ReadOnly<{
detail?: DetailType,
startTime?: DOMHighResTimeStamp,
}>;
export interface PerformanceMarkOptions {
+detail?: DetailType;
+startTime?: DOMHighResTimeStamp;
}
export type TimeStampOrName = DOMHighResTimeStamp | string;
export type PerformanceMeasureInit = $ReadOnly<{
detail?: DetailType,
startTime: DOMHighResTimeStamp,
duration: DOMHighResTimeStamp,
}>;
export interface PerformanceMeasureInit extends PerformanceEntryInit {
+detail?: DetailType;
}
class PerformanceMarkTemplate extends PerformanceEntry {
// We don't use private fields because they're significantly slower to
@@ -45,9 +45,8 @@ class PerformanceMarkTemplate extends PerformanceEntry {
// This constructor isn't really used. See `PerformanceMark` below.
constructor(markName: string, markOptions?: PerformanceMarkOptions) {
super({
super('mark', {
name: markName,
entryType: 'mark',
startTime: markOptions?.startTime ?? getCurrentTimeStamp(),
duration: 0,
});
@@ -72,8 +71,8 @@ export const PerformanceMark: typeof PerformanceMarkTemplate =
markName: string,
markOptions?: PerformanceMarkOptions,
) {
this.__name = markName;
this.__entryType = 'mark';
this.__name = markName;
this.__startTime = markOptions?.startTime ?? getCurrentTimeStamp();
this.__duration = 0;
@@ -89,15 +88,10 @@ class PerformanceMeasureTemplate extends PerformanceEntry {
__detail: DetailType;
// This constructor isn't really used. See `PerformanceMeasure` below.
constructor(measureName: string, measureOptions: PerformanceMeasureInit) {
super({
name: measureName,
entryType: 'measure',
startTime: measureOptions.startTime,
duration: measureOptions.duration,
});
constructor(init: PerformanceMeasureInit) {
super('measure', init);
this.__detail = measureOptions?.detail ?? null;
this.__detail = init?.detail ?? null;
}
get detail(): DetailType {
@@ -110,16 +104,27 @@ export const PerformanceMeasure: typeof PerformanceMeasureTemplate =
// $FlowExpectedError[incompatible-type]
function PerformanceMeasure(
this: PerformanceMeasureTemplate,
measureName: string,
measureOptions: PerformanceMeasureInit,
init: PerformanceMeasureInit,
) {
this.__name = measureName;
this.__entryType = 'measure';
this.__startTime = measureOptions.startTime;
this.__duration = measureOptions.duration;
this.__name = init.name;
this.__startTime = init.startTime;
this.__duration = init.duration;
this.__detail = measureOptions.detail ?? null;
this.__detail = init.detail ?? null;
};
// $FlowExpectedError[prop-missing]
PerformanceMeasure.prototype = PerformanceMeasureTemplate.prototype;
export const PerformanceMeasure_public: typeof PerformanceMeasure =
/* eslint-disable no-shadow */
// $FlowExpectedError[incompatible-type]
function PerformanceMeasure() {
throw new TypeError(
"Failed to construct 'PerformanceMeasure': Illegal constructor",
);
};
// $FlowExpectedError[prop-missing]
PerformanceMeasure_public.prototype = PerformanceMeasure.prototype;
@@ -10,24 +10,14 @@
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
import type Performance from 'react-native/src/private/webapis/performance/Performance';
import type {PerformanceObserverEntryList} from 'react-native/src/private/webapis/performance/PerformanceObserver';
import MaybeNativePerformance from '../specs/NativePerformance';
import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import {useState} from 'react';
import {Text, View} from 'react-native';
import setUpPerformanceObserver from 'react-native/src/private/setup/setUpPerformanceObserver';
import {PerformanceEventTiming} from 'react-native/src/private/webapis/performance/EventTiming';
import {PerformanceObserver} from 'react-native/src/private/webapis/performance/PerformanceObserver';
const NativePerformance = nullthrows(MaybeNativePerformance);
setUpPerformanceObserver();
declare var performance: Performance;
function sleep(ms: number) {
const end = performance.now() + ms;
while (performance.now() < end) {}
@@ -251,6 +241,21 @@ describe('Event Timing API', () => {
expect([...performance.eventCounts.values()]).toEqual([1, 1, 3]);
});
it('does NOT allow creating instances of PerformanceEventTiming directly', () => {
expect(() => {
return new PerformanceEventTiming();
}).toThrow(
"Failed to construct 'PerformanceEventTiming': Illegal constructor",
);
});
it('does NOT allow creating instances of EventCounts directly', () => {
expect(() => {
// $FlowExpectedError[cannot-resolve-name]
return new EventCounts();
}).toThrow("Failed to construct 'EventCounts': Illegal constructor");
});
describe('durationThreshold option', () => {
it('works when used with `type`', () => {
const callback = jest.fn();

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