diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 452a2afdc307b91..a431bc0a55c798a 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -93,7 +93,6 @@ export function getDefineEnv({ isNodeServer, isEdgeServer, middlewareMatchers, - hasServerComponents, }: { dev?: boolean distDir: string @@ -104,7 +103,6 @@ export function getDefineEnv({ isEdgeServer?: boolean middlewareMatchers?: MiddlewareMatcher[] config: NextConfigComplete - hasServerComponents?: boolean }) { return { // internal field to identify the plugin config @@ -178,7 +176,6 @@ export function getDefineEnv({ ), 'process.env.__NEXT_STRICT_MODE': JSON.stringify(config.reactStrictMode), 'process.env.__NEXT_REACT_ROOT': JSON.stringify(hasReactRoot), - 'process.env.__NEXT_RSC': JSON.stringify(hasServerComponents), 'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify( config.optimizeFonts && !dev ), @@ -540,15 +537,23 @@ export default async function getBaseWebpackConfig( rewrites.afterFiles.length > 0 || rewrites.fallback.length > 0 - if (isClient && !hasReactRoot) { - if (config.experimental.runtime) { - throw new Error( - '`experimental.runtime` requires React 18 to be installed.' - ) + // Only error in first one compiler (client) once + if (isClient) { + if (!hasReactRoot) { + if (config.experimental.runtime) { + throw new Error( + '`experimental.runtime` requires React 18 to be installed.' + ) + } + if (config.experimental.serverComponents) { + throw new Error( + '`experimental.serverComponents` requires React 18 to be installed.' + ) + } } - if (config.experimental.serverComponents) { + if (!config.experimental.appDir && config.experimental.serverComponents) { throw new Error( - '`experimental.serverComponents` requires React 18 to be installed.' + '`experimental.serverComponents` requires experimental.appDir to be enabled.' ) } } @@ -1680,7 +1685,6 @@ export default async function getBaseWebpackConfig( isNodeServer, isEdgeServer, middlewareMatchers, - hasServerComponents, }) ), isClient && diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index a4d62af4256a466..7324cccd7d90849 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -289,149 +289,6 @@ export async function initialize(opts: { webpackHMR?: any } = {}): Promise<{ return { assetPrefix: prefix } } -let RSCComponent: (props: any) => JSX.Element -if (process.env.__NEXT_RSC) { - const getCacheKey = () => { - const { pathname, search } = location - return pathname + search - } - - const { - createFromFetch, - createFromReadableStream, - } = require('next/dist/compiled/react-server-dom-webpack') - const encoder = new TextEncoder() - - let initialServerDataBuffer: string[] | undefined = undefined - let initialServerDataWriter: ReadableStreamDefaultController | undefined = - undefined - let initialServerDataLoaded = false - let initialServerDataFlushed = false - - function nextServerDataCallback(seg: [number, string, string]) { - if (seg[0] === 0) { - initialServerDataBuffer = [] - } else { - if (!initialServerDataBuffer) - throw new Error('Unexpected server data: missing bootstrap script.') - - if (initialServerDataWriter) { - initialServerDataWriter.enqueue(encoder.encode(seg[2])) - } else { - initialServerDataBuffer.push(seg[2]) - } - } - } - - // 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() - } - - const nextServerDataLoadingGlobal = ((self as any).__next_s = - (self as any).__next_s || []) - nextServerDataLoadingGlobal.forEach(nextServerDataCallback) - nextServerDataLoadingGlobal.push = nextServerDataCallback - - function createResponseCache() { - return new Map() - } - const rscCache = createResponseCache() - - function fetchFlight(href: string, props?: any) { - const url = new URL(href, location.origin) - const searchParams = url.searchParams - searchParams.append('__flight__', '1') - if (props) { - searchParams.append('__props__', JSON.stringify(props)) - } - return fetch(url.toString()) - } - - function useServerResponse(cacheKey: string, serialized?: string) { - let response = rscCache.get(cacheKey) - if (response) return response - - if (initialServerDataBuffer) { - const readable = new ReadableStream({ - start(controller) { - nextServerDataRegisterWriter(controller) - }, - }) - response = createFromReadableStream(readable) - } else { - if (serialized) { - const readable = new ReadableStream({ - start(controller) { - controller.enqueue(encoder.encode(serialized)) - controller.close() - }, - }) - response = createFromReadableStream(readable) - } else { - response = createFromFetch(fetchFlight(getCacheKey())) - } - } - - rscCache.set(cacheKey, response) - return response - } - - const ServerRoot = ({ - cacheKey, - serialized, - }: { - cacheKey: string - serialized?: string - }) => { - React.useEffect(() => { - rscCache.delete(cacheKey) - }) - const response = useServerResponse(cacheKey, serialized) - return response.readRoot() - } - - RSCComponent = (props: any) => { - const cacheKey = getCacheKey() - const { __flight__ } = props - return - } -} - function renderApp(App: AppComponent, appProps: AppProps) { return } @@ -691,18 +548,15 @@ function Root({ } function doRender(input: RenderRouteInfo): Promise { - let { App, Component, props, err, __N_RSC }: RenderRouteInfo = input + let { App, Component, props, err }: RenderRouteInfo = input let styleSheets: StyleSheetTuple[] | undefined = 'initial' in input ? undefined : input.styleSheets Component = Component || lastAppProps.Component props = props || lastAppProps.props - const isRSC = - process.env.__NEXT_RSC && 'initial' in input ? !!initialData.rsc : !!__N_RSC - const appProps: AppProps = { ...props, - Component: isRSC ? RSCComponent : Component, + Component, err, router, } @@ -1026,7 +880,6 @@ export async function hydrate(opts?: { beforeRender?: () => Promise }) { defaultLocale, domainLocales: initialData.domainLocales, isPreview: initialData.isPreview, - isRsc: initialData.rsc, }) initialMatchesMiddleware = await router._initialMatchesMiddlewarePromise diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 11cbc454c0cd7b3..d3176c140dd6565 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -526,7 +526,6 @@ export default class DevServer extends Server { hasReactRoot: this.hotReloader?.hasReactRoot, isNodeServer, isEdgeServer, - hasServerComponents: this.hotReloader?.hasServerComponents, }) Object.keys(plugin.definitions).forEach((key) => { diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index c352429e7bf6ddd..2ec63d476a1b39b 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -375,7 +375,6 @@ export async function renderToHTML( getStaticProps, getStaticPaths, getServerSideProps, - serverComponentManifest, isDataReq, params, previewProps, @@ -384,18 +383,11 @@ export async function renderToHTML( supportsDynamicHTML, images, runtime: globalRuntime, - ComponentMod, App, } = renderOpts let Document = renderOpts.Document - // We don't need to opt-into the flight inlining logic if the page isn't a RSC. - const isServerComponent = - !!process.env.__NEXT_REACT_ROOT && - !!serverComponentManifest && - !!ComponentMod.__next_rsc__?.server - // Component will be wrapped by ServerComponentWrapper for RSC let Component: React.ComponentType<{}> | ((props: any) => JSX.Element) = renderOpts.Component @@ -412,12 +404,6 @@ export async function renderToHTML( // next internal queries should be stripped out stripInternalQueries(query) - if (isServerComponent) { - throw new Error( - 'Server Components are not supported from the pages/ directory.' - ) - } - const callMiddleware = async (method: string, args: any[], props = false) => { let results: any = props ? {} : [] @@ -1417,7 +1403,6 @@ export async function renderToHTML( err: renderOpts.err ? serializeError(dev, renderOpts.err) : undefined, // Error if one happened, otherwise don't sent in the resulting HTML gsp: !!getStaticProps ? true : undefined, // whether the page is getStaticProps gssp: !!getServerSideProps ? true : undefined, // whether the page is getServerSideProps - rsc: isServerComponent ? true : undefined, // whether the page is a server components page customServer, // whether the user is using a custom server gip: hasPageGetInitialProps ? true : undefined, // whether the page has getInitialProps appGip: !defaultAppGetInitialProps ? true : undefined, // whether the _app has getInitialProps diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index 856c638eac756dd..c334cf38fd34b8e 100644 --- a/packages/next/shared/lib/constants.ts +++ b/packages/next/shared/lib/constants.ts @@ -86,7 +86,6 @@ export const TEMPORARY_REDIRECT_STATUS = 307 export const PERMANENT_REDIRECT_STATUS = 308 export const STATIC_PROPS_ID = '__N_SSG' export const SERVER_PROPS_ID = '__N_SSP' -export const FLIGHT_PROPS_ID = '__N_RSC' export const GOOGLE_FONT_PROVIDER = 'https://fonts.googleapis.com/' export const OPTIMIZED_FONT_PROVIDERS = [ { url: GOOGLE_FONT_PROVIDER, preconnect: 'https://fonts.gstatic.com' }, diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index 5fab0820e1b67f2..1ad9a039b42a2ed 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -564,7 +564,6 @@ export type CompletePrivateRouteInfo = { styleSheets: StyleSheetTuple[] __N_SSG?: boolean __N_SSP?: boolean - __N_RSC?: boolean props?: Record err?: Error error?: any @@ -881,7 +880,6 @@ export default class Router implements BaseRouter { defaultLocale, domainLocales, isPreview, - isRsc, }: { subscription: Subscription initialProps: any @@ -896,7 +894,6 @@ export default class Router implements BaseRouter { defaultLocale?: string domainLocales?: DomainLocale[] isPreview?: boolean - isRsc?: boolean } ) { // represents the current component key @@ -915,7 +912,6 @@ export default class Router implements BaseRouter { err, __N_SSG: initialProps && initialProps.__N_SSG, __N_SSP: initialProps && initialProps.__N_SSP, - __N_RSC: !!isRsc, } } @@ -1993,7 +1989,6 @@ export default class Router implements BaseRouter { styleSheets: res.styleSheets, __N_SSG: res.mod.__N_SSG, __N_SSP: res.mod.__N_SSP, - __N_RSC: !!res.mod.__next_rsc__, }) )) @@ -2006,20 +2001,10 @@ export default class Router implements BaseRouter { } } - /** - * For server components, non-SSR pages will have statically optimized - * flight data in a production build. So only development and SSR pages - * will always have the real-time generated and streamed flight data. - */ - const useStreamedFlightData = - routeInfo.__N_RSC && - (process.env.NODE_ENV !== 'production' || routeInfo.__N_SSP) - - const shouldFetchData = - routeInfo.__N_SSG || routeInfo.__N_SSP || routeInfo.__N_RSC + const shouldFetchData = routeInfo.__N_SSG || routeInfo.__N_SSP const { props, cacheKey } = await this._getData(async () => { - if (shouldFetchData && !useStreamedFlightData) { + if (shouldFetchData) { const { json, cacheKey: _cacheKey } = data?.json ? data : await fetchNextData({ @@ -2083,31 +2068,7 @@ export default class Router implements BaseRouter { ).catch(() => {}) } - let flightInfo - if (routeInfo.__N_RSC) { - flightInfo = { - __flight__: useStreamedFlightData - ? ( - await this._getData(() => - this._getFlightData( - formatWithValidation({ - query: { ...query, __flight__: '1' }, - pathname: isDynamicRoute(route) - ? interpolateAs( - pathname, - parseRelativeUrl(resolvedAs).pathname, - query - ).result - : pathname, - }) - ) - ) - ).data - : props.__flight__, - } - } - - props.pageProps = Object.assign({}, props.pageProps, flightInfo) + props.pageProps = Object.assign({}, props.pageProps) routeInfo.props = props routeInfo.route = route routeInfo.query = query diff --git a/packages/next/shared/lib/utils.ts b/packages/next/shared/lib/utils.ts index 76b9eee3a5207d9..67c486a83d1e447 100644 --- a/packages/next/shared/lib/utils.ts +++ b/packages/next/shared/lib/utils.ts @@ -109,7 +109,6 @@ export type NEXT_DATA = { scriptLoader?: any[] isPreview?: boolean notFoundSrcPage?: string - rsc?: boolean } /** @@ -177,7 +176,6 @@ export type AppPropsType< router: R __N_SSG?: boolean __N_SSP?: boolean - __N_RSC?: boolean } export type DocumentContext = NextPageContext & { diff --git a/packages/react-refresh-utils/internal/helpers.ts b/packages/react-refresh-utils/internal/helpers.ts index 7428b73196d1760..9ff15e9808c4989 100644 --- a/packages/react-refresh-utils/internal/helpers.ts +++ b/packages/react-refresh-utils/internal/helpers.ts @@ -48,7 +48,6 @@ function isSafeExport(key: string): boolean { key === '__esModule' || key === '__N_SSG' || key === '__N_SSP' || - key === '__N_RSC' || // TODO: remove this key from page config instead of allow listing it key === 'config' )