Repository navigation
Announce the JavaScript about to run with its URL and dev server revision - #59985
Open
RobertHerhold wants to merge 1 commit into
Open
RobertHerhold wants to merge 1 commit into
RobertHerhold wants to merge 1 commit into
Conversation
…sion
Summary:
Observers cannot tell which JavaScript runs in bridgeless mode, or which dev server revision it was built from. On iOS, `RCTJavaScriptDidLoadNotification` arrives on the main queue only after a bundle finishes evaluating and names it only through the bridge's current bundle URL, so a bundle that throws goes unseen, a reload in between is attributed to the new URL, and segments are never announced. On Android, the performance markers name a bundle only by its file's basename and a segment only by its ID. The dev server labels every bundle response with the revision of the module graph it was built from (`X-Metro-Delta-ID`) and every hot update with a `revisionId`, but React Native drops both.
iOS:
- `RCTInstance` posts `RCTJavaScriptWillStartExecutingNotification` synchronously right before a bundle runs (on the JS thread) and before a segment is registered (on the registering thread). `userInfo` carries the `bridge`, the bundle or segment file `url`, and the bundle's `revisionId` when the dev server sent one. `RCTJavaScriptDidLoadNotification` now carries the `url` too. `RCTConstants.h` states when each notification is posted, on which thread and with which keys, and that will-execute observers must not use UIKit or wait on the main thread. The macOS `RCTInstance` (`runtime/platform/macos`) is unchanged.
- `RCTInstance` calls `RCTMarkJavaScriptStartedExecuting()` right before each of those posts, and `RCTHasJavaScriptStartedExecuting()` reads it, so an observer that registers after JavaScript started can tell that it missed some instead of assuming none ran.
- `RCTJavaScriptLoader` keeps the `X-Metro-Delta-ID` header of a bundle it downloads as `RCTSource.revisionId` (nil for bundles read from disk or served without the header).
Android:
- `ReactJavaScriptLoads` (`UnstableReactNativeAPI`) calls its listeners right before `ReactInstance` runs a bundle and before it registers a segment file, on the thread that loads it, with the script's source URL, the file read (null for an APK asset) and the bundle's dev server revision. `ReactJavaScriptLoads.hasStartedExecuting()` is the late-listener check. A listener that throws is logged and does not stop the load or the other listeners.
- `BundleDownloader.BundleInfo.revisionId` keeps the `X-Metro-Delta-ID` of a downloaded bundle, `DevSupportManagerBase.downloadedJSBundleRevisionId` remembers it for the bundle last downloaded, and `ReactHostImpl` passes it to `JSBundleLoader.createCachedBundleFromNetworkLoader`, whose loader exposes it as `JSBundleLoader.revisionId`.
Both platforms:
- `HMRClient` calls `global.__HMR_UPDATE_REPORTER__?.onUpdate({revisionId})` for every update the server sends, after the client has applied it, or stashed it while Fast Refresh is off. An update that throws while it runs is not reported.
Nothing changes when no one observes the notification, adds a listener or installs the reporter.
Changelog:
[iOS][Added] - Post `RCTJavaScriptWillStartExecutingNotification` with the bundle or segment `url` and the dev server `revisionId` before JavaScript runs in bridgeless mode, add both to `RCTJavaScriptDidLoadNotification`, and expose a dev server bundle's revision as `RCTSource.revisionId`
[iOS][Added] - Add `RCTHasJavaScriptStartedExecuting()`, which tells an observer that registers late whether JavaScript already started executing
[Android][Added] - Add `ReactJavaScriptLoads`, which announces each bundle and segment the bridgeless runtime is about to run with its source URL, file and dev server revision, and expose a downloaded bundle's dev server revision as `JSBundleLoader.revisionId`
[General][Added] - Report each hot update's revision to an optional `global.__HMR_UPDATE_REPORTER__`
Differential Revision: D131359967
|
@RobertHerhold has exported this pull request. If you are a Meta employee, you can view the originating Diff in D131359967. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
Observers cannot tell which JavaScript runs in bridgeless mode, or which dev server revision it was built from. On iOS,
RCTJavaScriptDidLoadNotificationarrives on the main queue only after a bundle finishes evaluating and names it only through the bridge's current bundle URL, so a bundle that throws goes unseen, a reload in between is attributed to the new URL, and segments are never announced. On Android, the performance markers name a bundle only by its file's basename and a segment only by its ID. The dev server labels every bundle response with the revision of the module graph it was built from (X-Metro-Delta-ID) and every hot update with arevisionId, but React Native drops both.iOS:
RCTInstancepostsRCTJavaScriptWillStartExecutingNotificationsynchronously right before a bundle runs (on the JS thread) and before a segment is registered (on the registering thread).userInfocarries thebridge, the bundle or segment fileurl, and the bundle'srevisionIdwhen the dev server sent one.RCTJavaScriptDidLoadNotificationnow carries theurltoo.RCTConstants.hstates when each notification is posted, on which thread and with which keys, and that will-execute observers must not use UIKit or wait on the main thread. The macOSRCTInstance(runtime/platform/macos) is unchanged.RCTInstancecallsRCTMarkJavaScriptStartedExecuting()right before each of those posts, andRCTHasJavaScriptStartedExecuting()reads it, so an observer that registers after JavaScript started can tell that it missed some instead of assuming none ran.RCTJavaScriptLoaderkeeps theX-Metro-Delta-IDheader of a bundle it downloads asRCTSource.revisionId(nil for bundles read from disk or served without the header).Android:
ReactJavaScriptLoads(UnstableReactNativeAPI) calls its listeners right beforeReactInstanceruns a bundle and before it registers a segment file, on the thread that loads it, with the script's source URL, the file read (null for an APK asset) and the bundle's dev server revision.ReactJavaScriptLoads.hasStartedExecuting()is the late-listener check. A listener that throws is logged and does not stop the load or the other listeners.BundleDownloader.BundleInfo.revisionIdkeeps theX-Metro-Delta-IDof a downloaded bundle,DevSupportManagerBase.downloadedJSBundleRevisionIdremembers it for the bundle last downloaded, andReactHostImplpasses it toJSBundleLoader.createCachedBundleFromNetworkLoader, whose loader exposes it asJSBundleLoader.revisionId.Both platforms:
HMRClientcallsglobal.__HMR_UPDATE_REPORTER__?.onUpdate({revisionId})for every update the server sends, after the client has applied it, or stashed it while Fast Refresh is off. An update that throws while it runs is not reported.Nothing changes when no one observes the notification, adds a listener or installs the reporter.
Changelog:
[iOS][Added] - Post
RCTJavaScriptWillStartExecutingNotificationwith the bundle or segmenturland the dev serverrevisionIdbefore JavaScript runs in bridgeless mode, add both toRCTJavaScriptDidLoadNotification, and expose a dev server bundle's revision asRCTSource.revisionId[iOS][Added] - Add
RCTHasJavaScriptStartedExecuting(), which tells an observer that registers late whether JavaScript already started executing[Android][Added] - Add
ReactJavaScriptLoads, which announces each bundle and segment the bridgeless runtime is about to run with its source URL, file and dev server revision, and expose a downloaded bundle's dev server revision asJSBundleLoader.revisionId[General][Added] - Report each hot update's revision to an optional
global.__HMR_UPDATE_REPORTER__Differential Revision: D131359967