rsnext/packages/next/client/app-index.tsx

283 lines
8.7 KiB
TypeScript
Raw Normal View History

/* global location */
import '../build/polyfills/polyfill-module'
// @ts-ignore react-dom/client exists when using React 18
import ReactDOMClient from 'react-dom/client'
import React, { use } from 'react'
import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack/client'
Load `beforeInteractive` scripts properly without blocking hydration (#41164) This PR ensures that for the app directory, `beforeInteractive`, `afterInteractive` and `lazyOnload` scripts via `next/script` are properly supported. For both `beforeInteractive` and `afterInteractive` scripts, a preload link tag needs to be injected by Float. For `beforeInteractive` scripts and Next.js' polyfills, they need to be manually executed in order before starting the Next.js' runtime, without blocking the downloading of HTML and other scripts. This PR doesn't include the `worker` type of scripts yet. Note: in this PR I changed the inlined flight data `__next_s` to `__next_f`, and use `__next_s` for scripts data, because I can't find a better name for `next/script` that is also short at the same time. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-10-09 17:08:51 +02:00
import { HeadManagerContext } from '../shared/lib/head-manager-context'
import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context'
Implement loadable with lazy and suspense for next dynamic (#42589) ### Summary Migrate `next/dynamic` to implementation based on `React.lazy` and `Suspense`. Then it becomes easier to migrate the existing code in pages to layouts. Then we can support both `ssr` and `loading` option for `next/dynamic`. For `loading` option, it will work like `Suspense`'s `fallback` property ```js <Suspense fallback={loading}> <DynamicComponent /> </Suspense> ``` For `ssr` option, by default `React.lazy` supports SSR, but we'll disable the `ssr: false` case for dynamic import in server components since there's no client side involved. Then we don't need `suspense` option anymore as react >= 18 is always required. Mark it as deprecated. It also supports to load client component dynamically in server components now. #### Code code changes * switch loadable component to `lazy` + `Suspense` * will make sure it's retuning a module from `loader()` to `loader().then(mod => ({ default: mod.default || mod }))` since `lazy()` only accepts loader returning a module * Inside suspense boundary, throwing an error for ssr: false, catch the error on server and client side and ignore it. * Ignore options like ssr: false for server components since they're on server, doesn't make sense * Remove legacy dynamic related transform #### Feature changes * `next/dynamic` will work in the same way across the board (appDir and pages) * For the throwing error, will make it become a API that throws error later in the future, so users can customize more with `Suspense` * You can load client components now in server components with dynamic. Resolves #43147 #### Tests * existing dynamic tests all work * add case: import client component and load through next/dynamic in server components ### Issues
2022-12-07 19:42:10 +01:00
import { NEXT_DYNAMIC_NO_SSR_CODE } from '../shared/lib/no-ssr-error'
/// <reference types="react-dom/experimental" />
// Override chunk URL mapping in the webpack runtime
// https://github.com/webpack/webpack/blob/2738eebc7880835d88c727d364ad37f3ec557593/lib/RuntimeGlobals.js#L204
declare global {
const __webpack_require__: any
}
// eslint-disable-next-line no-undef
const getChunkScriptFilename = __webpack_require__.u
const chunkFilenameMap: any = {}
// eslint-disable-next-line no-undef
__webpack_require__.u = (chunkId: any) => {
return chunkFilenameMap[chunkId] || getChunkScriptFilename(chunkId)
}
// Ignore the module ID transform in client.
// eslint-disable-next-line no-undef
// @ts-expect-error TODO: fix type
Use proxy to ensure Flight is referencing to the latest module during development (#43823) As @timneutkens pointed out [here](https://github.com/vercel/next.js/pull/43819#discussion_r1042408158), changing the key will make Flight's module cache grow indefinitely. While I've thought about updating Flight's logic to clean the cache at some point, but that's tricky to do correctly as everything is asynchronous and we have to trigger clean up from outside (Webpack). So currently, a better way I can think of is to just give Flight a proxied object as `moduleExports`. Even if Flight is getting the same object from cache, it will always require the latest module from Webpack. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-08 20:00:52 +01:00
self.__next_require__ =
process.env.NODE_ENV !== 'production'
? (id: string) => {
const mod = __webpack_require__(id)
if (typeof mod === 'object') {
// Return a proxy to flight client to make sure it's always getting
// the latest module, instead of being cached.
return new Proxy(mod, {
get(_target, prop) {
return __webpack_require__(id)[prop]
},
})
}
return mod
}
: __webpack_require__
// eslint-disable-next-line no-undef
;(self as any).__next_chunk_load__ = (chunk: string) => {
if (!chunk) return Promise.resolve()
const [chunkId, chunkFileName] = chunk.split(':')
chunkFilenameMap[chunkId] = `static/chunks/${chunkFileName}.js`
// @ts-ignore
// eslint-disable-next-line no-undef
return __webpack_chunk_load__(chunkId)
}
const appElement: HTMLElement | Document | null = document
const getCacheKey = () => {
const { pathname, search } = location
return pathname + search
}
const encoder = new TextEncoder()
let initialServerDataBuffer: string[] | undefined = undefined
let initialServerDataWriter: ReadableStreamDefaultController | undefined =
undefined
let initialServerDataLoaded = false
let initialServerDataFlushed = false
function nextServerDataCallback(
seg: [isBootStrap: 0] | [isNotBootstrap: 1, responsePartial: string]
): void {
if (seg[0] === 0) {
initialServerDataBuffer = []
} else {
if (!initialServerDataBuffer)
throw new Error('Unexpected server data: missing bootstrap script.')
if (initialServerDataWriter) {
initialServerDataWriter.enqueue(encoder.encode(seg[1]))
} else {
initialServerDataBuffer.push(seg[1])
}
}
}
// There might be race conditions between `nextServerDataRegisterWriter` and
// `DOMContentLoaded`. The former will be called when React starts to hydrate
// the root, the latter will be called when the DOM is fully loaded.
// For streaming, the former is called first due to partial hydration.
// For non-streaming, the latter can be called first.
// Hence, we use two variables `initialServerDataLoaded` and
// `initialServerDataFlushed` to make sure the writer will be closed and
// `initialServerDataBuffer` will be cleared in the right time.
function nextServerDataRegisterWriter(ctr: ReadableStreamDefaultController) {
if (initialServerDataBuffer) {
initialServerDataBuffer.forEach((val) => {
ctr.enqueue(encoder.encode(val))
})
if (initialServerDataLoaded && !initialServerDataFlushed) {
ctr.close()
initialServerDataFlushed = true
initialServerDataBuffer = undefined
}
}
initialServerDataWriter = ctr
}
// When `DOMContentLoaded`, we can close all pending writers to finish hydration.
const DOMContentLoaded = function () {
if (initialServerDataWriter && !initialServerDataFlushed) {
initialServerDataWriter.close()
initialServerDataFlushed = true
initialServerDataBuffer = undefined
}
initialServerDataLoaded = true
}
// It's possible that the DOM is already loaded.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', DOMContentLoaded, false)
} else {
DOMContentLoaded()
}
Implement loadable with lazy and suspense for next dynamic (#42589) ### Summary Migrate `next/dynamic` to implementation based on `React.lazy` and `Suspense`. Then it becomes easier to migrate the existing code in pages to layouts. Then we can support both `ssr` and `loading` option for `next/dynamic`. For `loading` option, it will work like `Suspense`'s `fallback` property ```js <Suspense fallback={loading}> <DynamicComponent /> </Suspense> ``` For `ssr` option, by default `React.lazy` supports SSR, but we'll disable the `ssr: false` case for dynamic import in server components since there's no client side involved. Then we don't need `suspense` option anymore as react >= 18 is always required. Mark it as deprecated. It also supports to load client component dynamically in server components now. #### Code code changes * switch loadable component to `lazy` + `Suspense` * will make sure it's retuning a module from `loader()` to `loader().then(mod => ({ default: mod.default || mod }))` since `lazy()` only accepts loader returning a module * Inside suspense boundary, throwing an error for ssr: false, catch the error on server and client side and ignore it. * Ignore options like ssr: false for server components since they're on server, doesn't make sense * Remove legacy dynamic related transform #### Feature changes * `next/dynamic` will work in the same way across the board (appDir and pages) * For the throwing error, will make it become a API that throws error later in the future, so users can customize more with `Suspense` * You can load client components now in server components with dynamic. Resolves #43147 #### Tests * existing dynamic tests all work * add case: import client component and load through next/dynamic in server components ### Issues
2022-12-07 19:42:10 +01:00
function onRecoverableError(err: any) {
// Using default react onRecoverableError
// x-ref: https://github.com/facebook/react/blob/d4bc16a7d69eb2ea38a88c8ac0b461d5f72cdcab/packages/react-dom/src/client/ReactDOMRoot.js#L83
const defaultOnRecoverableError =
typeof reportError === 'function'
? // In modern browsers, reportError will dispatch an error event,
// emulating an uncaught JavaScript error.
reportError
: (error: any) => {
window.console.error(error)
}
// Skip certain custom errors which are not expected to be reported on client
if (err.digest === NEXT_DYNAMIC_NO_SSR_CODE) return
defaultOnRecoverableError(err)
}
Load `beforeInteractive` scripts properly without blocking hydration (#41164) This PR ensures that for the app directory, `beforeInteractive`, `afterInteractive` and `lazyOnload` scripts via `next/script` are properly supported. For both `beforeInteractive` and `afterInteractive` scripts, a preload link tag needs to be injected by Float. For `beforeInteractive` scripts and Next.js' polyfills, they need to be manually executed in order before starting the Next.js' runtime, without blocking the downloading of HTML and other scripts. This PR doesn't include the `worker` type of scripts yet. Note: in this PR I changed the inlined flight data `__next_s` to `__next_f`, and use `__next_s` for scripts data, because I can't find a better name for `next/script` that is also short at the same time. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-10-09 17:08:51 +02:00
const nextServerDataLoadingGlobal = ((self as any).__next_f =
(self as any).__next_f || [])
nextServerDataLoadingGlobal.forEach(nextServerDataCallback)
nextServerDataLoadingGlobal.push = nextServerDataCallback
function createResponseCache() {
return new Map<string, any>()
}
const rscCache = createResponseCache()
2022-09-19 14:17:20 +02:00
function useInitialServerResponse(cacheKey: string): Promise<JSX.Element> {
const response = rscCache.get(cacheKey)
if (response) return response
const readable = new ReadableStream({
start(controller) {
nextServerDataRegisterWriter(controller)
},
})
const newResponse = createFromReadableStream(readable)
rscCache.set(cacheKey, newResponse)
return newResponse
}
2022-09-19 14:17:20 +02:00
function ServerRoot({ cacheKey }: { cacheKey: string }): JSX.Element {
React.useEffect(() => {
rscCache.delete(cacheKey)
})
const response = useInitialServerResponse(cacheKey)
const root = use(response)
return root
}
const StrictModeIfEnabled = process.env.__NEXT_STRICT_MODE_APP
? React.StrictMode
: React.Fragment
function Root({ children }: React.PropsWithChildren<{}>): React.ReactElement {
React.useEffect(() => {
Optimize bundle size for appDir (#42252) - Ensure React / other deps are deduped against main-app bundle - Only require web-vitals when it's needed - Move warnOnce into separate file as it's not tree shaken - Add create-next-app with bundle analyzer <details> <summary>13.0.0 (react-dom deduping bug)</summary> <img width="1912" alt="13-0-0" src="https://user-images.githubusercontent.com/6324199/199067942-d2394ffa-fc1c-4606-94f4-b489ef959a9b.png"> </details> <details> <summary>Canary (react-dom deduping bug fixed)</summary> <img width="1912" alt="current-canary-branch" src="https://user-images.githubusercontent.com/6324199/199067999-786b523e-b4f0-4044-8d2e-acaa0386771a.png"> </details> <details> <summary>This PR</summary> <img width="1912" alt="pr-changes" src="https://user-images.githubusercontent.com/6324199/199068060-2fdb6e11-8b94-4683-9b8e-65bb6faab34a.png"> </details> ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-10-31 18:50:35 +01:00
if (process.env.__NEXT_ANALYTICS_ID) {
require('./performance-relayer-app')()
}
}, [])
if (process.env.__NEXT_TEST_MODE) {
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useEffect(() => {
window.__NEXT_HYDRATED = true
if (window.__NEXT_HYDRATED_CB) {
window.__NEXT_HYDRATED_CB()
}
}, [])
}
return children as React.ReactElement
}
2022-09-19 14:17:20 +02:00
function RSCComponent(props: any): JSX.Element {
const cacheKey = getCacheKey()
return <ServerRoot {...props} cacheKey={cacheKey} />
}
export function hydrate() {
if (process.env.NODE_ENV !== 'production') {
const rootLayoutMissingTagsError = (self as any)
.__next_root_layout_missing_tags_error
const HotReload: typeof import('./components/react-dev-overlay/hot-reloader-client').default =
require('./components/react-dev-overlay/hot-reloader-client')
.default as typeof import('./components/react-dev-overlay/hot-reloader-client').default
// Don't try to hydrate if root layout is missing required tags, render error instead
if (rootLayoutMissingTagsError) {
const reactRootElement = document.createElement('div')
document.body.appendChild(reactRootElement)
Implement loadable with lazy and suspense for next dynamic (#42589) ### Summary Migrate `next/dynamic` to implementation based on `React.lazy` and `Suspense`. Then it becomes easier to migrate the existing code in pages to layouts. Then we can support both `ssr` and `loading` option for `next/dynamic`. For `loading` option, it will work like `Suspense`'s `fallback` property ```js <Suspense fallback={loading}> <DynamicComponent /> </Suspense> ``` For `ssr` option, by default `React.lazy` supports SSR, but we'll disable the `ssr: false` case for dynamic import in server components since there's no client side involved. Then we don't need `suspense` option anymore as react >= 18 is always required. Mark it as deprecated. It also supports to load client component dynamically in server components now. #### Code code changes * switch loadable component to `lazy` + `Suspense` * will make sure it's retuning a module from `loader()` to `loader().then(mod => ({ default: mod.default || mod }))` since `lazy()` only accepts loader returning a module * Inside suspense boundary, throwing an error for ssr: false, catch the error on server and client side and ignore it. * Ignore options like ssr: false for server components since they're on server, doesn't make sense * Remove legacy dynamic related transform #### Feature changes * `next/dynamic` will work in the same way across the board (appDir and pages) * For the throwing error, will make it become a API that throws error later in the future, so users can customize more with `Suspense` * You can load client components now in server components with dynamic. Resolves #43147 #### Tests * existing dynamic tests all work * add case: import client component and load through next/dynamic in server components ### Issues
2022-12-07 19:42:10 +01:00
const reactRoot = (ReactDOMClient as any).createRoot(reactRootElement, {
onRecoverableError,
})
reactRoot.render(
<GlobalLayoutRouterContext.Provider
value={{
tree: rootLayoutMissingTagsError.tree,
changeByServerResponse: () => {},
focusAndScrollRef: {
apply: false,
},
}}
>
<HotReload
assetPrefix={rootLayoutMissingTagsError.assetPrefix}
// initialState={{
// rootLayoutMissingTagsError: {
// missingTags: rootLayoutMissingTagsError.missingTags,
// },
// }}
/>
</GlobalLayoutRouterContext.Provider>
)
return
}
}
const reactEl = (
<StrictModeIfEnabled>
Load `beforeInteractive` scripts properly without blocking hydration (#41164) This PR ensures that for the app directory, `beforeInteractive`, `afterInteractive` and `lazyOnload` scripts via `next/script` are properly supported. For both `beforeInteractive` and `afterInteractive` scripts, a preload link tag needs to be injected by Float. For `beforeInteractive` scripts and Next.js' polyfills, they need to be manually executed in order before starting the Next.js' runtime, without blocking the downloading of HTML and other scripts. This PR doesn't include the `worker` type of scripts yet. Note: in this PR I changed the inlined flight data `__next_s` to `__next_f`, and use `__next_s` for scripts data, because I can't find a better name for `next/script` that is also short at the same time. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-10-09 17:08:51 +02:00
<HeadManagerContext.Provider
value={{
appDir: true,
}}
>
<Root>
<RSCComponent />
</Root>
</HeadManagerContext.Provider>
</StrictModeIfEnabled>
)
Implement loadable with lazy and suspense for next dynamic (#42589) ### Summary Migrate `next/dynamic` to implementation based on `React.lazy` and `Suspense`. Then it becomes easier to migrate the existing code in pages to layouts. Then we can support both `ssr` and `loading` option for `next/dynamic`. For `loading` option, it will work like `Suspense`'s `fallback` property ```js <Suspense fallback={loading}> <DynamicComponent /> </Suspense> ``` For `ssr` option, by default `React.lazy` supports SSR, but we'll disable the `ssr: false` case for dynamic import in server components since there's no client side involved. Then we don't need `suspense` option anymore as react >= 18 is always required. Mark it as deprecated. It also supports to load client component dynamically in server components now. #### Code code changes * switch loadable component to `lazy` + `Suspense` * will make sure it's retuning a module from `loader()` to `loader().then(mod => ({ default: mod.default || mod }))` since `lazy()` only accepts loader returning a module * Inside suspense boundary, throwing an error for ssr: false, catch the error on server and client side and ignore it. * Ignore options like ssr: false for server components since they're on server, doesn't make sense * Remove legacy dynamic related transform #### Feature changes * `next/dynamic` will work in the same way across the board (appDir and pages) * For the throwing error, will make it become a API that throws error later in the future, so users can customize more with `Suspense` * You can load client components now in server components with dynamic. Resolves #43147 #### Tests * existing dynamic tests all work * add case: import client component and load through next/dynamic in server components ### Issues
2022-12-07 19:42:10 +01:00
const options = {
onRecoverableError,
}
const isError = document.documentElement.id === '__next_error__'
const reactRoot = isError
Implement loadable with lazy and suspense for next dynamic (#42589) ### Summary Migrate `next/dynamic` to implementation based on `React.lazy` and `Suspense`. Then it becomes easier to migrate the existing code in pages to layouts. Then we can support both `ssr` and `loading` option for `next/dynamic`. For `loading` option, it will work like `Suspense`'s `fallback` property ```js <Suspense fallback={loading}> <DynamicComponent /> </Suspense> ``` For `ssr` option, by default `React.lazy` supports SSR, but we'll disable the `ssr: false` case for dynamic import in server components since there's no client side involved. Then we don't need `suspense` option anymore as react >= 18 is always required. Mark it as deprecated. It also supports to load client component dynamically in server components now. #### Code code changes * switch loadable component to `lazy` + `Suspense` * will make sure it's retuning a module from `loader()` to `loader().then(mod => ({ default: mod.default || mod }))` since `lazy()` only accepts loader returning a module * Inside suspense boundary, throwing an error for ssr: false, catch the error on server and client side and ignore it. * Ignore options like ssr: false for server components since they're on server, doesn't make sense * Remove legacy dynamic related transform #### Feature changes * `next/dynamic` will work in the same way across the board (appDir and pages) * For the throwing error, will make it become a API that throws error later in the future, so users can customize more with `Suspense` * You can load client components now in server components with dynamic. Resolves #43147 #### Tests * existing dynamic tests all work * add case: import client component and load through next/dynamic in server components ### Issues
2022-12-07 19:42:10 +01:00
? (ReactDOMClient as any).createRoot(appElement, options)
: (React as any).startTransition(() =>
Implement loadable with lazy and suspense for next dynamic (#42589) ### Summary Migrate `next/dynamic` to implementation based on `React.lazy` and `Suspense`. Then it becomes easier to migrate the existing code in pages to layouts. Then we can support both `ssr` and `loading` option for `next/dynamic`. For `loading` option, it will work like `Suspense`'s `fallback` property ```js <Suspense fallback={loading}> <DynamicComponent /> </Suspense> ``` For `ssr` option, by default `React.lazy` supports SSR, but we'll disable the `ssr: false` case for dynamic import in server components since there's no client side involved. Then we don't need `suspense` option anymore as react >= 18 is always required. Mark it as deprecated. It also supports to load client component dynamically in server components now. #### Code code changes * switch loadable component to `lazy` + `Suspense` * will make sure it's retuning a module from `loader()` to `loader().then(mod => ({ default: mod.default || mod }))` since `lazy()` only accepts loader returning a module * Inside suspense boundary, throwing an error for ssr: false, catch the error on server and client side and ignore it. * Ignore options like ssr: false for server components since they're on server, doesn't make sense * Remove legacy dynamic related transform #### Feature changes * `next/dynamic` will work in the same way across the board (appDir and pages) * For the throwing error, will make it become a API that throws error later in the future, so users can customize more with `Suspense` * You can load client components now in server components with dynamic. Resolves #43147 #### Tests * existing dynamic tests all work * add case: import client component and load through next/dynamic in server components ### Issues
2022-12-07 19:42:10 +01:00
(ReactDOMClient as any).hydrateRoot(appElement, reactEl, options)
)
if (isError) {
reactRoot.render(reactEl)
}
}