Skip to content

Commit

Permalink
Drop legacy RSC handling in client for pages (#40472)
Browse files Browse the repository at this point in the history
Remove the unused RSC handling for pages in both server router and client side in favor of using RSC in app dir
  • Loading branch information
huozhi committed Sep 12, 2022
1 parent f92a4ce commit c7d3f9d
Show file tree
Hide file tree
Showing 8 changed files with 20 additions and 222 deletions.
26 changes: 15 additions & 11 deletions packages/next/build/webpack-config.ts
Expand Up @@ -93,7 +93,6 @@ export function getDefineEnv({
isNodeServer,
isEdgeServer,
middlewareMatchers,
hasServerComponents,
}: {
dev?: boolean
distDir: string
Expand All @@ -104,7 +103,6 @@ export function getDefineEnv({
isEdgeServer?: boolean
middlewareMatchers?: MiddlewareMatcher[]
config: NextConfigComplete
hasServerComponents?: boolean
}) {
return {
// internal field to identify the plugin config
Expand Down Expand Up @@ -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
),
Expand Down Expand Up @@ -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.'
)
}
}
Expand Down Expand Up @@ -1680,7 +1685,6 @@ export default async function getBaseWebpackConfig(
isNodeServer,
isEdgeServer,
middlewareMatchers,
hasServerComponents,
})
),
isClient &&
Expand Down
151 changes: 2 additions & 149 deletions packages/next/client/index.tsx
Expand Up @@ -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<string, any>()
}
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 <ServerRoot cacheKey={cacheKey} serialized={__flight__} />
}
}

function renderApp(App: AppComponent, appProps: AppProps) {
return <App {...appProps} />
}
Expand Down Expand Up @@ -691,18 +548,15 @@ function Root({
}

function doRender(input: RenderRouteInfo): Promise<any> {
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,
}
Expand Down Expand Up @@ -1026,7 +880,6 @@ export async function hydrate(opts?: { beforeRender?: () => Promise<void> }) {
defaultLocale,
domainLocales: initialData.domainLocales,
isPreview: initialData.isPreview,
isRsc: initialData.rsc,
})

initialMatchesMiddleware = await router._initialMatchesMiddlewarePromise
Expand Down
1 change: 0 additions & 1 deletion packages/next/server/dev/next-dev-server.ts
Expand Up @@ -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) => {
Expand Down
15 changes: 0 additions & 15 deletions packages/next/server/render.tsx
Expand Up @@ -375,7 +375,6 @@ export async function renderToHTML(
getStaticProps,
getStaticPaths,
getServerSideProps,
serverComponentManifest,
isDataReq,
params,
previewProps,
Expand All @@ -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
Expand All @@ -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 ? {} : []

Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion packages/next/shared/lib/constants.ts
Expand Up @@ -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' },
Expand Down

0 comments on commit c7d3f9d

Please sign in to comment.