From bcfe8f2e7682e6861cdda2d9f41a94e7f7d26342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 7 Apr 2025 05:15:37 -0700 Subject: [PATCH] Remove unnecessary Event and CustomEvent polyfills (#50495) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50495 Changelog: [internal] This migrates the last remaining usages of these polyfills to the built-in classes. Reviewed By: yungsters Differential Revision: D67828652 fbshipit-source-id: 5ff039d7413aed49d882ff03e722531cdd195488 --- .../Libraries/Events/CustomEvent.js | 32 --- .../Libraries/Events/EventPolyfill.js | 239 ------------------ .../ReactNativePrivateInterface.js | 4 +- .../__snapshots__/public-api-test.js.snap | 77 ------ 4 files changed, 2 insertions(+), 350 deletions(-) delete mode 100644 packages/react-native/Libraries/Events/CustomEvent.js delete mode 100644 packages/react-native/Libraries/Events/EventPolyfill.js diff --git a/packages/react-native/Libraries/Events/CustomEvent.js b/packages/react-native/Libraries/Events/CustomEvent.js deleted file mode 100644 index 41aa04fbd20..00000000000 --- a/packages/react-native/Libraries/Events/CustomEvent.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow strict-local - */ - -// Make sure global Event is defined -import EventPolyfill from './EventPolyfill'; - -type CustomEvent$Options = $ReadOnly<{ - bubbles?: boolean, - cancelable?: boolean, - composed?: boolean, - detail?: {...}, -}>; - -class CustomEvent extends EventPolyfill { - detail: ?{...}; - - constructor(typeArg: string, options: CustomEvent$Options) { - const {bubbles, cancelable, composed} = options; - super(typeArg, {bubbles, cancelable, composed}); - - this.detail = options.detail; // this would correspond to `NativeEvent` in NativeSyntheticEvent - } -} - -export default CustomEvent; diff --git a/packages/react-native/Libraries/Events/EventPolyfill.js b/packages/react-native/Libraries/Events/EventPolyfill.js deleted file mode 100644 index 659058afa8e..00000000000 --- a/packages/react-native/Libraries/Events/EventPolyfill.js +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow strict-local - */ - -// https://dom.spec.whatwg.org/#dictdef-eventinit -type Event$Init = { - bubbles?: boolean, - cancelable?: boolean, - composed?: boolean, - /** Non-standard. See `composed` instead. */ - scoped?: boolean, - ... -}; - -/** - * This is a copy of the Event interface defined in Flow: - * https://github.com/facebook/flow/blob/741104e69c43057ebd32804dd6bcc1b5e97548ea/lib/dom.js - * which is itself a faithful interface of the W3 spec: - * https://dom.spec.whatwg.org/#interface-event - * - * Since Flow assumes that Event is provided and is on the global object, - * we must provide an implementation of Event for CustomEvent (and future - * alignment of React Native's event system with the W3 spec). - */ -interface IEvent { - constructor(type: string, eventInitDict?: Event$Init): void; - /** - * Returns the type of event, e.g. "click", "hashchange", or "submit". - */ - +type: string; - /** - * Returns the object to which event is dispatched (its target). - */ - +target: EventTarget; // TODO: nullable - /** @deprecated */ - +srcElement: Element; // TODO: nullable - /** - * Returns the object whose event listener's callback is currently being invoked. - */ - +currentTarget: EventTarget; // TODO: nullable - /** - * Returns the invocation target objects of event's path (objects on which - * listeners will be invoked), except for any nodes in shadow trees of which - * the shadow root's mode is "closed" that are not reachable from event's - * currentTarget. - */ - composedPath(): Array; - - +NONE: number; - +AT_TARGET: number; - +BUBBLING_PHASE: number; - +CAPTURING_PHASE: number; - /** - * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, - * and BUBBLING_PHASE. - */ - +eventPhase: number; - - /** - * When dispatched in a tree, invoking this method prevents event from reaching - * any objects other than the current object. - */ - stopPropagation(): void; - /** - * Invoking this method prevents event from reaching any registered event - * listeners after the current one finishes running and, when dispatched in a - * tree, also prevents event from reaching any other objects. - */ - stopImmediatePropagation(): void; - - /** - * Returns true or false depending on how event was initialized. True if - * event goes through its target's ancestors in reverse tree order, and - * false otherwise. - */ - +bubbles: boolean; - /** - * Returns true or false depending on how event was initialized. Its - * return value does not always carry meaning, but true can indicate - * that part of the operation during which event was dispatched, can - * be canceled by invoking the preventDefault() method. - */ - +cancelable: boolean; - // returnValue: boolean; // legacy, and some subclasses still define it as a string! - /** - * If invoked when the cancelable attribute value is true, and while - * executing a listener for the event with passive set to false, signals to - * the operation that caused event to be dispatched that it needs to be - * canceled. - */ - preventDefault(): void; - /** - * Returns true if preventDefault() was invoked successfully to indicate - * cancelation, and false otherwise. - */ - +defaultPrevented: boolean; - /** - * Returns true or false depending on how event was initialized. True if - * event invokes listeners past a ShadowRoot node that is the root of its - * target, and false otherwise. - */ - +composed: boolean; - - /** - * Returns true if event was dispatched by the user agent, and false otherwise. - */ - +isTrusted: boolean; - /** - * Returns the event's timestamp as the number of milliseconds measured relative - * to the time origin. - */ - +timeStamp: number; - - /** Non-standard. See Event.prototype.composedPath */ - +deepPath?: () => EventTarget[]; - /** Non-standard. See Event.prototype.composed */ - +scoped: boolean; - - /** - * @deprecated - */ - initEvent(type: string, bubbles: boolean, cancelable: boolean): void; -} - -class EventPolyfill implements IEvent { - type: string; - bubbles: boolean; - cancelable: boolean; - composed: boolean; - // Non-standard. See `composed` instead. - scoped: boolean; - isTrusted: boolean; - defaultPrevented: boolean; - timeStamp: number; - - // https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase - NONE: number; - AT_TARGET: number; - BUBBLING_PHASE: number; - CAPTURING_PHASE: number; - - eventPhase: number; - - currentTarget: EventTarget; // TODO: nullable - target: EventTarget; // TODO: nullable - /** @deprecated */ - srcElement: Element; // TODO: nullable - - // React Native-specific: proxy data to a NativeSyntheticEvent when - // certain methods are called. - // NativeSyntheticEvent will also have a reference to this instance - - // it is circular - and both classes use this reference to keep - // data with the other in sync. - _syntheticEvent: mixed; - - constructor(type: string, eventInitDict?: Event$Init) { - this.type = type; - this.bubbles = !!(eventInitDict?.bubbles || false); - this.cancelable = !!(eventInitDict?.cancelable || false); - this.composed = !!(eventInitDict?.composed || false); - this.scoped = !!(eventInitDict?.scoped || false); - - // TODO: somehow guarantee that only "private" instantiations of Event - // can set this to true - this.isTrusted = false; - - // TODO: in the future we'll want to make sure this has the same - // time-basis as events originating from native - this.timeStamp = Date.now(); - - this.defaultPrevented = false; - - // https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase - this.NONE = 0; - this.AT_TARGET = 1; - this.BUBBLING_PHASE = 2; - this.CAPTURING_PHASE = 3; - this.eventPhase = this.NONE; - - // $FlowFixMe - this.currentTarget = null; - // $FlowFixMe - this.target = null; - // $FlowFixMe - this.srcElement = null; - } - - composedPath(): Array { - throw new Error('TODO: not yet implemented'); - } - - preventDefault(): void { - this.defaultPrevented = true; - - if (this._syntheticEvent != null) { - // $FlowFixMe - this._syntheticEvent.preventDefault(); - } - } - - initEvent(type: string, bubbles: boolean, cancelable: boolean): void { - throw new Error( - 'TODO: not yet implemented. This method is also deprecated.', - ); - } - - stopImmediatePropagation(): void { - throw new Error('TODO: not yet implemented'); - } - - stopPropagation(): void { - if (this._syntheticEvent != null) { - // $FlowFixMe - this._syntheticEvent.stopPropagation(); - } - } - - setSyntheticEvent(value: mixed): void { - this._syntheticEvent = value; - } -} - -// Assertion magic for polyfill follows. -declare var checkEvent: Event; // eslint-disable-line no-unused-vars - -/*:: -// This can be a strict mode error at runtime so put it in a Flow comment. -(checkEvent: IEvent); -*/ - -global.Event = EventPolyfill; - -export default EventPolyfill; diff --git a/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js b/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js index ed7bf10c35a..fc049635438 100644 --- a/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js +++ b/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js @@ -8,6 +8,7 @@ * @flow strict-local */ +import typeof CustomEvent from '../../src/private/webapis/dom/events/CustomEvent'; import typeof BatchedBridge from '../BatchedBridge/BatchedBridge'; import typeof legacySendAccessibilityEvent from '../Components/AccessibilityInfo/legacySendAccessibilityEvent'; import typeof TextInputState from '../Components/TextInput/TextInputState'; @@ -15,7 +16,6 @@ import typeof ExceptionsManager from '../Core/ExceptionsManager'; import typeof RawEventEmitter from '../Core/RawEventEmitter'; import typeof ReactFiberErrorDialog from '../Core/ReactFiberErrorDialog'; import typeof RCTEventEmitter from '../EventEmitter/RCTEventEmitter'; -import typeof CustomEvent from '../Events/CustomEvent'; import typeof { createPublicInstance, createPublicRootInstance, @@ -99,7 +99,7 @@ module.exports = { return require('../Core/RawEventEmitter').default; }, get CustomEvent(): CustomEvent { - return require('../Events/CustomEvent').default; + return require('../../src/private/webapis/dom/events/CustomEvent').default; }, get createAttributePayload(): createAttributePayload { return require('../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload') diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 9e5a42915be..f55228581ed 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -4166,83 +4166,6 @@ declare export default typeof RCTNativeAppEventEmitter; " `; -exports[`public API should not change unintentionally Libraries/Events/CustomEvent.js 1`] = ` -"type CustomEvent$Options = $ReadOnly<{ - bubbles?: boolean, - cancelable?: boolean, - composed?: boolean, - detail?: { ... }, -}>; -declare class CustomEvent extends EventPolyfill { - detail: ?{ ... }; - constructor(typeArg: string, options: CustomEvent$Options): void; -} -declare export default typeof CustomEvent; -" -`; - -exports[`public API should not change unintentionally Libraries/Events/EventPolyfill.js 1`] = ` -"type Event$Init = { - bubbles?: boolean, - cancelable?: boolean, - composed?: boolean, - scoped?: boolean, - ... -}; -interface IEvent { - constructor(type: string, eventInitDict?: Event$Init): void; - +type: string; - +target: EventTarget; - +srcElement: Element; - +currentTarget: EventTarget; - composedPath(): Array; - +NONE: number; - +AT_TARGET: number; - +BUBBLING_PHASE: number; - +CAPTURING_PHASE: number; - +eventPhase: number; - stopPropagation(): void; - stopImmediatePropagation(): void; - +bubbles: boolean; - +cancelable: boolean; - preventDefault(): void; - +defaultPrevented: boolean; - +composed: boolean; - +isTrusted: boolean; - +timeStamp: number; - +deepPath?: () => EventTarget[]; - +scoped: boolean; - initEvent(type: string, bubbles: boolean, cancelable: boolean): void; -} -declare class EventPolyfill implements IEvent { - type: string; - bubbles: boolean; - cancelable: boolean; - composed: boolean; - scoped: boolean; - isTrusted: boolean; - defaultPrevented: boolean; - timeStamp: number; - NONE: number; - AT_TARGET: number; - BUBBLING_PHASE: number; - CAPTURING_PHASE: number; - eventPhase: number; - currentTarget: EventTarget; - target: EventTarget; - srcElement: Element; - constructor(type: string, eventInitDict?: Event$Init): void; - composedPath(): Array; - preventDefault(): void; - initEvent(type: string, bubbles: boolean, cancelable: boolean): void; - stopImmediatePropagation(): void; - stopPropagation(): void; - setSyntheticEvent(value: mixed): void; -} -declare export default typeof EventPolyfill; -" -`; - exports[`public API should not change unintentionally Libraries/Image/AssetRegistry.js 1`] = ` "declare const AssetRegistry: { registerAsset: (asset: PackagerAsset) => number,