|
| 1 | +import * as ReactDOM from 'react-dom'; |
| 2 | +import type { ReactElement } from 'react'; |
| 3 | +import type { RenderReturnType } from './types/index.ts' with { 'resolution-mode': 'import' }; |
| 4 | + |
| 5 | +const reactMajorVersion = Number(ReactDOM.version?.split('.')[0]) || 16; |
| 6 | + |
| 7 | +// TODO: once we require React 18, we can remove this and inline everything guarded by it. |
| 8 | +export const supportsRootApi = reactMajorVersion >= 18; |
| 9 | + |
| 10 | +// TODO: once React dependency is updated to >= 18, we can remove this and just |
| 11 | +// import ReactDOM from 'react-dom/client'; |
| 12 | +let reactDomClient: typeof import('react-dom/client'); |
| 13 | +if (supportsRootApi) { |
| 14 | + // This will never throw an exception, but it's the way to tell Webpack the dependency is optional |
| 15 | + // https://github.com/webpack/webpack/issues/339#issuecomment-47739112 |
| 16 | + // Unfortunately, it only converts the error to a warning. |
| 17 | + try { |
| 18 | + // eslint-disable-next-line global-require,@typescript-eslint/no-require-imports |
| 19 | + reactDomClient = require('react-dom/client') as typeof import('react-dom/client'); |
| 20 | + } catch (_e) { |
| 21 | + // We should never get here, but if we do, we'll just use the default ReactDOM |
| 22 | + // and live with the warning. |
| 23 | + reactDomClient = ReactDOM as unknown as typeof import('react-dom/client'); |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +type HydrateOrRenderType = (domNode: Element, reactElement: ReactElement) => RenderReturnType; |
| 28 | + |
| 29 | +/* eslint-disable @typescript-eslint/no-deprecated,@typescript-eslint/no-non-null-assertion -- |
| 30 | + * while we need to support React 16 |
| 31 | + */ |
| 32 | +const hydrateProp = 'hydrate'; |
| 33 | +const renderProp = 'render'; |
| 34 | + |
| 35 | +export const reactHydrate: HydrateOrRenderType = supportsRootApi |
| 36 | + ? reactDomClient!.hydrateRoot |
| 37 | + : (domNode, reactElement) => ReactDOM[hydrateProp](reactElement, domNode); |
| 38 | + |
| 39 | +export function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType { |
| 40 | + if (supportsRootApi) { |
| 41 | + const root = reactDomClient!.createRoot(domNode); |
| 42 | + root.render(reactElement); |
| 43 | + return root; |
| 44 | + } |
| 45 | + |
| 46 | + return ReactDOM[renderProp](reactElement, domNode); |
| 47 | +} |
| 48 | +/* eslint-enable @typescript-eslint/no-deprecated,@typescript-eslint/no-non-null-assertion */ |
0 commit comments