From 35495d6565940ea90177b8d8e0dafaab944e1a8e Mon Sep 17 00:00:00 2001 From: Felipe Mota Date: Tue, 26 Nov 2024 19:12:15 -0300 Subject: [PATCH] fix(react-v19): Properly support react v19 Make prepass backwards compatible with react v19, it was necessary to handle with the following changes in React: - https://github.com/facebook/react/pull/28789/ - https://github.com/facebook/react/pull/28813 - https://github.com/facebook/react/pull/28226 --- .tool-versions | 2 +- src/element.js | 8 ++++++-- src/symbols.js | 23 ++++++++++++++++++----- src/types/element.js | 5 +++-- src/visitor.js | 36 ++++++++++++++++++++++++++---------- 5 files changed, 54 insertions(+), 20 deletions(-) diff --git a/.tool-versions b/.tool-versions index 1c3e8cf..d064b3e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 14.20.0 +nodejs 18.19.0 diff --git a/src/element.js b/src/element.js index de237ea..017ca15 100644 --- a/src/element.js +++ b/src/element.js @@ -6,6 +6,7 @@ import type { AbstractContext, AbstractElement } from './types' import { type ReactSymbol, REACT_ELEMENT_TYPE, + REACT_TRANSITIONAL_ELEMENT_TYPE, REACT_PORTAL_TYPE, REACT_FRAGMENT_TYPE, REACT_STRICT_MODE_TYPE, @@ -16,7 +17,8 @@ import { REACT_FORWARD_REF_TYPE, REACT_SUSPENSE_TYPE, REACT_MEMO_TYPE, - REACT_LAZY_TYPE + REACT_LAZY_TYPE, + REACT_CONSUMER_TYPE } from './symbols' /** Is a given Component a class component */ @@ -29,6 +31,7 @@ export const typeOf = (x: AbstractElement): ReactSymbol | void => { case REACT_PORTAL_TYPE: return REACT_PORTAL_TYPE case REACT_ELEMENT_TYPE: + case REACT_TRANSITIONAL_ELEMENT_TYPE: switch (x.type) { case REACT_CONCURRENT_MODE_TYPE: return REACT_CONCURRENT_MODE_TYPE @@ -47,8 +50,9 @@ export const typeOf = (x: AbstractElement): ReactSymbol | void => { return REACT_LAZY_TYPE case REACT_MEMO_TYPE: return REACT_MEMO_TYPE + case REACT_CONSUMER_TYPE: + return REACT_CONSUMER_TYPE case REACT_CONTEXT_TYPE: - return REACT_CONTEXT_TYPE case REACT_PROVIDER_TYPE: return REACT_PROVIDER_TYPE case REACT_FORWARD_REF_TYPE: diff --git a/src/symbols.js b/src/symbols.js index adf4172..fb60633 100644 --- a/src/symbols.js +++ b/src/symbols.js @@ -2,13 +2,19 @@ import type { Node } from 'react' +/** + * Element is already legacy in NextJS v15 https://github.com/vercel/next.js/pull/65058 + * https://github.com/facebook/react/pull/28813 + */ let Element = 0xeac7 +let TransitionalElement = 0xeac7 let Portal = 0xeaca let Fragment = 0xeacb let StrictMode = 0xeacc let Profiler = 0xead2 let ContextProvider = 0xeacd -let ContextConsumer = 0xeace +let ContextConsumer = undefined +let Context = 0xeace let ConcurrentMode = 0xeacf let ForwardRef = 0xead0 let Suspense = 0xead1 @@ -19,28 +25,32 @@ let ClientReferenceTag = undefined if (typeof Symbol === 'function' && Symbol.for) { const symbolFor = Symbol.for Element = symbolFor('react.element') + TransitionalElement = symbolFor('react.transitional.element') Portal = symbolFor('react.portal') Fragment = symbolFor('react.fragment') StrictMode = symbolFor('react.strict_mode') Profiler = symbolFor('react.profiler') ContextProvider = symbolFor('react.provider') - ContextConsumer = symbolFor('react.context') - ConcurrentMode = Symbol.for('react.concurrent_mode') + ContextConsumer = symbolFor('react.consumer') + Context = symbolFor('react.context') + ConcurrentMode = symbolFor('react.concurrent_mode') ForwardRef = symbolFor('react.forward_ref') Suspense = symbolFor('react.suspense') Memo = symbolFor('react.memo') Lazy = symbolFor('react.lazy') - ClientReferenceTag = Symbol.for('react.client.reference') + ClientReferenceTag = symbolFor('react.client.reference') } /** Literal types representing the ReactSymbol values. These values do not actually match the values from react-is! */ export type ReactSymbol = | 'react.element' /* 0xeac7 | Symbol(react.element) */ + | 'react.transitional.element' /* 0xeac7 | Symbol(react.transitional.element) */ | 'react.portal' /* 0xeaca | Symbol(react.portal) */ | 'react.fragment' /* 0xeacb | Symbol(react.fragment) */ | 'react.strict_mode' /* 0xeacc | Symbol(react.strict_mode) */ | 'react.profiler' /* 0xead2 | Symbol(react.profiler) */ | 'react.provider' /* 0xeacd | Symbol(react.provider) */ + | 'react.consumer' /* undefined | Symbol(react.consumer) */ | 'react.context' /* 0xeace | Symbol(react.context) */ | 'react.concurrent_mode' /* 0xeacf | Symbol(react.concurrent_mode) */ | 'react.forward_ref' /* 0xead0 | Symbol(react.forward_ref) */ @@ -49,12 +59,15 @@ export type ReactSymbol = | 'react.lazy' /* 0xead4 | Symbol(react.lazy) */ export const REACT_ELEMENT_TYPE: 'react.element' = (Element: any) +export const REACT_TRANSITIONAL_ELEMENT_TYPE: 'react.transitional.element' = + (TransitionalElement: any) export const REACT_PORTAL_TYPE: 'react.portal' = (Portal: any) export const REACT_FRAGMENT_TYPE: 'react.fragment' = (Fragment: any) export const REACT_STRICT_MODE_TYPE: 'react.strict_mode' = (StrictMode: any) export const REACT_PROFILER_TYPE: 'react.profiler' = (Profiler: any) export const REACT_PROVIDER_TYPE: 'react.provider' = (ContextProvider: any) -export const REACT_CONTEXT_TYPE: 'react.context' = (ContextConsumer: any) +export const REACT_CONSUMER_TYPE: 'react.consumer' = (ContextConsumer: any) +export const REACT_CONTEXT_TYPE: 'react.context' = (Context: any) export const REACT_CONCURRENT_MODE_TYPE: 'react.concurrent_mode' = (ConcurrentMode: any) export const REACT_FORWARD_REF_TYPE: 'react.forward_ref' = (ForwardRef: any) diff --git a/src/types/element.js b/src/types/element.js index 28af34d..d39c211 100644 --- a/src/types/element.js +++ b/src/types/element.js @@ -14,7 +14,8 @@ import { REACT_FORWARD_REF_TYPE, REACT_SUSPENSE_TYPE, REACT_MEMO_TYPE, - REACT_LAZY_TYPE + REACT_LAZY_TYPE, + REACT_CONSUMER_TYPE } from '../symbols' export type AbstractContext = Context & { @@ -41,7 +42,7 @@ export type ConsumerElement = { type: | AbstractContext | { - $$typeof: typeof REACT_CONTEXT_TYPE, + $$typeof: typeof REACT_CONSUMER_TYPE, _context: AbstractContext }, props: { children?: (value: mixed) => Node }, diff --git a/src/visitor.js b/src/visitor.js index 16d2439..b19acac 100644 --- a/src/visitor.js +++ b/src/visitor.js @@ -66,6 +66,7 @@ import { import { REACT_ELEMENT_TYPE, + REACT_TRANSITIONAL_ELEMENT_TYPE, REACT_PORTAL_TYPE, REACT_FRAGMENT_TYPE, REACT_STRICT_MODE_TYPE, @@ -76,7 +77,8 @@ import { REACT_FORWARD_REF_TYPE, REACT_SUSPENSE_TYPE, REACT_MEMO_TYPE, - REACT_LAZY_TYPE + REACT_LAZY_TYPE, + REACT_CONSUMER_TYPE } from './symbols' import { isClientReference } from './utils' @@ -86,7 +88,19 @@ const REACT_INTERNALS = (React: any).__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE const ReactCurrentDispatcher = - REACT_INTERNALS && REACT_INTERNALS.ReactCurrentDispatcher + REACT_INTERNALS.ReactCurrentDispatcher || REACT_INTERNALS + +const getReactCurrentDispatcher = () => { + return ReactCurrentDispatcher.current || ReactCurrentDispatcher.H +} + +const injectReactCurrentDispatcher = (newDispatcher) => { + if (ReactCurrentDispatcher.current) { + ReactCurrentDispatcher.current = newDispatcher + } else { + ReactCurrentDispatcher.H = newDispatcher + } +} // In the presence of setImmediate, i.e. on Node, we'll enable the // yielding behavior that gives the event loop a chance to continue @@ -141,12 +155,14 @@ export const visitElement = ( const providerElement = ((element: any): ProviderElement) // Add provider's value prop to context const { value, children } = providerElement.props - setContextValue(providerElement.type._context, value) + const type = (providerElement.type: any) + const context = typeof type._context === 'object' ? type._context : type + setContextValue(context, value) return getChildrenArray(children) } - case REACT_CONTEXT_TYPE: { + case REACT_CONSUMER_TYPE: { const consumerElement = ((element: any): ConsumerElement) const { children } = consumerElement.props @@ -221,11 +237,11 @@ const visitLoop = ( visitor: Visitor, clientRefVisitor: ClientReferenceVisitor ): boolean => { - const prevDispatcher = ReactCurrentDispatcher.current + const prevDispatcher = getReactCurrentDispatcher() const start = Date.now() try { - ReactCurrentDispatcher.current = Dispatcher + injectReactCurrentDispatcher(Dispatcher) while (traversalChildren.length > 0) { const element = traversalChildren[traversalChildren.length - 1].shift() if (element !== undefined) { @@ -254,7 +270,7 @@ const visitLoop = ( queue.unshift(errorFrame) return false } finally { - ReactCurrentDispatcher.current = prevDispatcher + injectReactCurrentDispatcher(prevDispatcher) } } @@ -341,10 +357,10 @@ export const update = ( ) } } else { - const prevDispatcher = ReactCurrentDispatcher.current + const prevDispatcher = getReactCurrentDispatcher() let children = null - ReactCurrentDispatcher.current = Dispatcher + injectReactCurrentDispatcher(Dispatcher) try { if (frame.kind === 'frame.class') { @@ -363,7 +379,7 @@ export const update = ( queue.unshift(errorFrame) children = null } finally { - ReactCurrentDispatcher.current = prevDispatcher + injectReactCurrentDispatcher(prevDispatcher) } visit(getChildrenArray(children), queue, visitor, clientRefVisitor)