diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index c6ff90de1178..525fe5c29f1d 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -24,8 +24,6 @@ import type { ReactReadableStream } from './node-web-streams-helper' import type { ServerRuntime } from './config-shared' import React from 'react' -import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack' -import { renderToReadableStream } from 'next/dist/compiled/react-server-dom-webpack/writer.browser.server' import { StyleRegistry, createStyleRegistry } from 'styled-jsx' import { GSP_NO_RETURNED_VALUE, @@ -70,8 +68,6 @@ import RenderResult from './render-result' import isError from '../lib/is-error' import { readableStreamTee, - encodeText, - decodeText, streamFromArray, streamToString, chainStreams, @@ -81,13 +77,11 @@ import { } from './node-web-streams-helper' import { ImageConfigContext } from '../shared/lib/image-config-context' import stripAnsi from 'next/dist/compiled/strip-ansi' -import { urlQueryToSearchParams } from '../shared/lib/router/utils/querystring' -import { postProcessHTML } from './post-process' -import { htmlEscapeJsonString } from './htmlescape' import { shouldUseReactRoot, stripInternalQueries } from './utils' let tryGetPreviewData: typeof import('./api-utils/node').tryGetPreviewData let warn: typeof import('../build/output/log').warn +let postProcessHTML: typeof import('./post-process').postProcessHTML const DOCTYPE = '' const ReactDOMServer = shouldUseReactRoot @@ -98,8 +92,10 @@ if (process.env.NEXT_RUNTIME !== 'edge') { require('./node-polyfill-web-streams') tryGetPreviewData = require('./api-utils/node').tryGetPreviewData warn = require('../build/output/log').warn + postProcessHTML = require('./post-process').postProcessHTML } else { warn = console.warn.bind(console) + postProcessHTML = async (_pathname: string, html: string) => html } function noRouter() { @@ -311,115 +307,6 @@ function checkRedirectValues( } } -const rscCache = new Map() - -function useFlightResponse({ - cachePrefix, - req, - pageData, - inlinedDataWritable, - serverComponentManifest, -}: { - cachePrefix: string - req: ReadableStream - pageData: { current: string } | null - inlinedDataWritable: WritableStream - serverComponentManifest: any -}) { - const id = cachePrefix + ',' + (React as any).useId() - let entry = rscCache.get(id) - if (!entry) { - const [renderStream, forwardStream] = readableStreamTee(req) - - entry = createFromReadableStream(renderStream, { - moduleMap: serverComponentManifest.__ssr_module_mapping__, - }) - rscCache.set(id, entry) - - let bootstrapped = false - - const forwardReader = forwardStream.getReader() - const inlinedDataWriter = inlinedDataWritable.getWriter() - - function process() { - forwardReader.read().then(({ done, value }) => { - if (!bootstrapped) { - bootstrapped = true - inlinedDataWriter.write( - encodeText( - `` - ) - ) - } - if (done) { - rscCache.delete(id) - inlinedDataWriter.close() - } else { - const decodedValue = decodeText(value) - inlinedDataWriter.write( - encodeText( - `` - ) - ) - if (pageData) { - pageData.current += decodedValue - } - process() - } - }) - } - process() - } - return entry -} - -// Create the wrapper component for a Flight stream. -function createServerComponentRenderer( - Component: any, - { - cachePrefix, - pageData, - inlinedTransformStream, - serverComponentManifest, - }: { - cachePrefix: string - pageData: { current: string } | null - inlinedTransformStream: TransformStream - serverComponentManifest: NonNullable - } -) { - function ServerComponentWrapper({ router, ...props }: any) { - const reqStream: ReadableStream = renderToReadableStream( - , - serverComponentManifest - ) - - const response = useFlightResponse({ - cachePrefix, - req: reqStream, - pageData, - inlinedDataWritable: inlinedTransformStream.writable, - serverComponentManifest, - }) - - const root = response.readRoot() - return root - } - - for (const methodName of Object.keys(Component)) { - const method = (Component as any)[methodName] - if (method) { - ;(ServerComponentWrapper as any)[methodName] = method - } - } - - return ServerComponentWrapper -} - export async function renderToHTML( req: IncomingMessage, res: ServerResponse, @@ -479,41 +366,16 @@ export async function renderToHTML( Uint8Array > | null = null - const serverComponentsStaticPageData: { current: string } | null = - isServerComponent && process.env.NEXT_RUNTIME !== 'edge' - ? { current: '' } - : null - - let renderServerComponentData = isServerComponent - ? query.__flight__ !== undefined - : false - - const serverComponentProps = - isServerComponent && query.__props__ - ? JSON.parse(query.__props__ as string) - : undefined - const isFallback = !!query.__nextFallback const notFoundSrcPage = query.__nextNotFoundSrcPage + // next internal queries should be stripped out stripInternalQueries(query) - // next internal queries should be stripped out if (isServerComponent) { - serverComponentsInlinedTransformStream = new TransformStream() - const search = urlQueryToSearchParams(query).toString() - - // @ts-ignore - globalThis.__next_require__ = ComponentMod.__next_rsc__.__webpack_require__ - // @ts-ignore - globalThis.__next_chunk_load__ = () => Promise.resolve() - - Component = createServerComponentRenderer(Component, { - cachePrefix: pathname + (search ? `?${search}` : ''), - inlinedTransformStream: serverComponentsInlinedTransformStream, - pageData: serverComponentsStaticPageData, - serverComponentManifest, - }) + throw new Error( + 'Server Components are not supported from the pages/ directory.' + ) } const callMiddleware = async (method: string, args: any[], props = false) => { @@ -572,8 +434,7 @@ export async function renderToHTML( !hasPageGetInitialProps && defaultAppGetInitialProps && !isSSG && - !getServerSideProps && - !isServerComponent + !getServerSideProps if (hasPageGetInitialProps && isSSG) { throw new Error(SSG_GET_INITIAL_PROPS_CONFLICT + ` ${pathname}`) @@ -1165,11 +1026,7 @@ export async function renderToHTML( // Avoid rendering page un-necessarily for getServerSideProps data request // and getServerSideProps/getStaticProps redirects if ((isDataReq && !isSSG) || (renderOpts as any).isRedirect) { - // For server components, we still need to render the page to get the flight - // data. - if (!serverComponentsStaticPageData) { - return RenderResult.fromStatic(JSON.stringify(props)) - } + return RenderResult.fromStatic(JSON.stringify(props)) } // We don't call getStaticProps or getServerSideProps while generating @@ -1178,23 +1035,9 @@ export async function renderToHTML( props.pageProps = {} } - // Pass router to the Server Component as a temporary workaround. - if (isServerComponent) { - props.pageProps = Object.assign({}, props.pageProps) - } - // the response might be finished on the getInitialProps call if (isResSent(res) && !isSSG) return null - if (renderServerComponentData) { - return new RenderResult( - renderToReadableStream( - , - serverComponentManifest - ).pipeThrough(createBufferedTransformStream()) - ) - } - // we preload the buildManifest for auto-export dynamic pages // to speed up hydrating query values let filteredBuildManifest = buildManifest @@ -1259,10 +1102,7 @@ export async function renderToHTML( } } - if ( - (isServerComponent || process.env.NEXT_RUNTIME === 'edge') && - Document.getInitialProps - ) { + if (process.env.NEXT_RUNTIME === 'edge' && Document.getInitialProps) { if (BuiltinFunctionalDocument) { Document = BuiltinFunctionalDocument } else { @@ -1433,9 +1273,7 @@ export async function renderToHTML( } const hasDocumentGetInitialProps = !( - isServerComponent || - process.env.NEXT_RUNTIME === 'edge' || - !Document.getInitialProps + process.env.NEXT_RUNTIME === 'edge' || !Document.getInitialProps ) let bodyResult: (s: string) => Promise> @@ -1460,11 +1298,11 @@ export async function renderToHTML( const { docProps } = (documentInitialPropsRes as any) || {} const documentElement = () => { - if (isServerComponent || process.env.NEXT_RUNTIME === 'edge') { + if (process.env.NEXT_RUNTIME === 'edge') { return (Document as any)() + } else { + return } - - return } let styles @@ -1633,32 +1471,11 @@ export async function renderToHTML( await documentResult.bodyResult(renderTargetSuffix), ] - // After the page is fully rendered, we can assign the flight response to - // page data of `renderOpts` now. - function updateServerComponentPageData() { - if (serverComponentsStaticPageData) { - ;(renderOpts as any).pageData = { - ...(renderOpts as any).pageData, - __flight__: serverComponentsStaticPageData.current, - } - } - } - - if ( - serverComponentsStaticPageData && - ((isDataReq && !isSSG) || (renderOpts as any).isRedirect) - ) { - await streamToString(streams[1]) - updateServerComponentPageData() - return RenderResult.fromStatic((renderOpts as any).pageData) - } - const postOptimize = (html: string) => postProcessHTML(pathname, html, renderOpts, { inAmpMode, hybridAmp }) if (generateStaticHTML) { const html = await streamToString(chainStreams(streams)) - updateServerComponentPageData() const optimizedHtml = await postOptimize(html) return new RenderResult(optimizedHtml) } diff --git a/test/production/react-18-streaming-ssr/custom-server/next.config.js b/test/production/react-18-streaming-ssr/custom-server/next.config.js index 060d50f525d6..4ba52ba2c8df 100644 --- a/test/production/react-18-streaming-ssr/custom-server/next.config.js +++ b/test/production/react-18-streaming-ssr/custom-server/next.config.js @@ -1,5 +1 @@ -module.exports = { - experimental: { - serverComponents: true, - }, -} +module.exports = {} diff --git a/test/production/react-18-streaming-ssr/custom-server/pages/index.server.js b/test/production/react-18-streaming-ssr/custom-server/pages/index.js similarity index 100% rename from test/production/react-18-streaming-ssr/custom-server/pages/index.server.js rename to test/production/react-18-streaming-ssr/custom-server/pages/index.js diff --git a/test/production/react-18-streaming-ssr/custom-server/server.js b/test/production/react-18-streaming-ssr/custom-server/server.js index c7ccbd8df6be..c8619cd29f5f 100644 --- a/test/production/react-18-streaming-ssr/custom-server/server.js +++ b/test/production/react-18-streaming-ssr/custom-server/server.js @@ -34,7 +34,6 @@ server.listen(currentPort, (err) => { distDir: '.next', experimental: { ...defaultNextConfig.experimental, - serverComponents: true, }, }, }) diff --git a/test/production/react-18-streaming-ssr/index.test.ts b/test/production/react-18-streaming-ssr/index.test.ts index f42652bf7911..bc4fb8df0c54 100644 --- a/test/production/react-18-streaming-ssr/index.test.ts +++ b/test/production/react-18-streaming-ssr/index.test.ts @@ -14,7 +14,7 @@ const react18Deps = { 'react-dom': '^18.0.0', } -describe('react 18 streaming SSR and RSC in minimal mode', () => { +describe('react 18 streaming SSR in minimal mode', () => { let next: NextInstance beforeAll(async () => { @@ -22,7 +22,7 @@ describe('react 18 streaming SSR and RSC in minimal mode', () => { next = await createNext({ files: { - 'pages/index.server.js': ` + 'pages/index.js': ` export default function Page() { return

streaming

} @@ -33,7 +33,6 @@ describe('react 18 streaming SSR and RSC in minimal mode', () => { }, nextConfig: { experimental: { - serverComponents: true, runtime: 'nodejs', }, webpack(config, { nextRuntime }) { @@ -124,7 +123,7 @@ describe('react 18 streaming SSR with custom next configs', () => { }) }) -describe('react 18 streaming SSR and RSC with custom server', () => { +describe('react 18 streaming SSR with custom server', () => { let next let server let appPort @@ -158,7 +157,7 @@ describe('react 18 streaming SSR and RSC with custom server', () => { await next.destroy() if (server) await killApp(server) }) - it('should render rsc correctly under custom server', async () => { + it('should render page correctly under custom server', async () => { const html = await renderViaHTTP(appPort, '/') expect(html).toContain('streaming') })