Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop legacy RSC handling in client for pages #40472

Merged
merged 9 commits into from Sep 12, 2022
Merged
4 changes: 0 additions & 4 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 @@ -1680,7 +1677,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.'
huozhi marked this conversation as resolved.
Show resolved Hide resolved
)
}

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
45 changes: 3 additions & 42 deletions packages/next/shared/lib/router/router.ts
Expand Up @@ -564,7 +564,6 @@ export type CompletePrivateRouteInfo = {
styleSheets: StyleSheetTuple[]
__N_SSG?: boolean
__N_SSP?: boolean
__N_RSC?: boolean
props?: Record<string, any>
err?: Error
error?: any
Expand Down Expand Up @@ -881,7 +880,6 @@ export default class Router implements BaseRouter {
defaultLocale,
domainLocales,
isPreview,
isRsc,
}: {
subscription: Subscription
initialProps: any
Expand All @@ -896,7 +894,6 @@ export default class Router implements BaseRouter {
defaultLocale?: string
domainLocales?: DomainLocale[]
isPreview?: boolean
isRsc?: boolean
}
) {
// represents the current component key
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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__,
})
))

Expand All @@ -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({
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions packages/next/shared/lib/utils.ts
Expand Up @@ -109,7 +109,6 @@ export type NEXT_DATA = {
scriptLoader?: any[]
isPreview?: boolean
notFoundSrcPage?: string
rsc?: boolean
}

/**
Expand Down Expand Up @@ -177,7 +176,6 @@ export type AppPropsType<
router: R
__N_SSG?: boolean
__N_SSP?: boolean
__N_RSC?: boolean
}

export type DocumentContext = NextPageContext & {
Expand Down
1 change: 0 additions & 1 deletion packages/react-refresh-utils/internal/helpers.ts
Expand Up @@ -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'
)
Expand Down