From fc3ef810ba3cfddd7763ad6fc498a6f2cddc3064 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 11 Sep 2022 11:39:32 -0700 Subject: [PATCH 1/6] Update dev watcher to ignore more accurately (#40412) This ensures we ignore all except the files/directories we are explicitly wanting to watch to prevent watching/considering directories with massive amounts of files slowing down startup. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` Fixes: https://github.com/vercel/next.js/pull/38483#issuecomment-1242154665 --- packages/next/server/dev/next-dev-server.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index d360778905f4..11cbc454c0cd 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -273,12 +273,10 @@ export default class DevServer extends Server { }) } - const wp = (this.webpackWatcher = new Watchpack({ - ignored: /([/\\]node_modules[/\\]|[/\\]\.next[/\\]|[/\\]\.git[/\\])/, - })) const pages = this.pagesDir ? [this.pagesDir] : [] const app = this.appDir ? [this.appDir] : [] const directories = [...pages, ...app] + const files = this.pagesDir ? getPossibleMiddlewareFilenames( pathJoin(this.pagesDir, '..'), @@ -303,6 +301,15 @@ export default class DevServer extends Server { ] files.push(...tsconfigPaths) + const wp = (this.webpackWatcher = new Watchpack({ + ignored: (pathname: string) => { + return ( + !files.some((file) => file.startsWith(pathname)) && + !directories.some((dir) => pathname.startsWith(dir)) + ) + }, + })) + wp.watch({ directories: [this.dir], startTime: 0 }) const fileWatchTimes = new Map() let enabledTypeScript = this.usingTypeScript From 40fc066f09c0a987d563e3b869f48c388dd797aa Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 11 Sep 2022 19:12:14 -0500 Subject: [PATCH 2/6] Update Server Components documentation. (#40452) This PR updates the server components documentation to make it more clear the benefit of server components and what their current status is inside Next.js. It removes code snippets and CTAs for trying them out, as things have changed with the Layouts RFC since these docs were previously written. With the upcoming changes, server components will now be the default inside `app`. The list of supported and unsupported features was no longer accurate. We will be providing more guidance as the layouts and routing changes are ready to be tested. [Read the Layouts RFC](https://nextjs.org/blog/layouts-rfc) for more details. --- .../react-18/server-components.md | 115 ++---------------- 1 file changed, 13 insertions(+), 102 deletions(-) diff --git a/docs/advanced-features/react-18/server-components.md b/docs/advanced-features/react-18/server-components.md index 906dab699bff..027a7d6690b1 100644 --- a/docs/advanced-features/react-18/server-components.md +++ b/docs/advanced-features/react-18/server-components.md @@ -1,116 +1,27 @@ # React Server Components (RFC) -Server Components allow us to render React components on the server. This is fundamentally different from server-side rendering (SSR) where you're pre-generating HTML on the server. With Server Components, there's **zero client-side JavaScript needed,** making page rendering faster. This improves the user experience of your application, pairing the best parts of server-rendering with client-side interactivity. +React Server Components allow developers to build applications that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. -### Next Router and Layouts RFC +In an upcoming Next.js release, React and Next.js developers will be able to use Server Components inside the `app` directory as part of the changes outlined by the [Layouts RFC](https://nextjs.org/blog/layouts-rfc). -We are currently implementing the [Next.js Router and Layouts RFC](https://nextjs.org/blog/layouts-rfc). +## What are React Server Components? -The new Next.js router will be built on top of React 18 features, including React Server Components. +React Server Components improve the user experience of your application by pairing the best parts of server-rendering with client-side interactivity. -One of the biggest proposed changes is that, by default, files inside a new `app` directory will be rendered on the server as React Server Components. +With traditional React applications that are client-side only, developers often had to make tradeoffs between SEO and performance. Server Components enable developers to better leverage their server infrastructure and achieve great performance by default. -This will allow you to automatically adopt React Server Components when migrating from `pages` to `app`. +For example, large dependencies that previously would impact the JavaScript bundle size on the client can instead stay entirely on the server. By sending less JavaScript to the browser, the time to interactive for the page is decreased, leading to improved [Core Web Vitals](https://vercel.com/blog/core-web-vitals). -You can find more information on the [RFC](https://nextjs.org/blog/layouts-rfc) and we welcome your feedback on [Github Discussions](https://github.com/vercel/next.js/discussions/37136). +## React Server Components vs Server-Side Rendering -### Server Components Conventions +[Server-side Rendering](/docs/basic-features/pages.md#server-side-rendering) (SSR) dynamically builds your application into HTML on the server. This creates faster load times for users by offloading work from the user's device to the server, especially those with slower internet connections or older devices. However, developers still pay the cost to download, parse, and hydrate those components after the initial HTML loads. -To run a component on the server, append `.server.js` to the end of the filename. For example, `./pages/home.server.js` will be treated as a Server Component. +React Server Components, combined with Next.js server-side rendering, help eliminate the tradeoff of all-or-nothing data fetching. You can progressively show updates as your data comes in. -For client components, append `.client.js` to the filename. For example, `./components/avatar.client.js`. +## Using React Server Components with Next.js -Server components can import server components and client components. +The Next.js team at Vercel released the [Layouts RFC](https://nextjs.org/blog/layouts-rfc) a few months ago outlining the vision for the future of routing, layouts, and data fetching in the framework. These changes **aren't available yet**, but we can start learning about how they will be used. -Client components **cannot** import server components. +Pages and Layouts in `app` will be rendered as React Server Components by default. This improves performance by reducing the amount of JavaScript sent to the client for components that are not interactive. Client components will be able to be defined through either a file name extension or through a string literal in the file. -Components without a `server` or `client` extension will be treated as shared components and can be imported by server components and client components. For example: - -```jsx -// pages/home.server.js - -import { Suspense } from 'react' - -import Profile from '../components/profile.server' -import Content from '../components/content.client' - -export default function Home() { - return ( -
-

Welcome to React Server Components

- - - - -
- ) -} -``` - -The `` and `` components will always be server-side rendered and streamed to the client, and will not be included by the client-side JavaScript. However, `` will still be hydrated on the client-side, like normal React components. - -> Make sure you're using default imports and exports for server components (`.server.js`). The support of named exports are a work in progress! - -To see a full example, check out the [vercel/next-react-server-components demo](https://github.com/vercel/next-react-server-components). - -## Supported Next.js APIs - -### `next/link` and `next/image` - -You can use `next/link` and `next/image` like before and they will be treated as client components to keep the interaction on client side. - -### `next/document` - -If you have a custom `_document`, you have to change your `_document` to a functional component like below to use server components. If you don't have one, Next.js will use the default `_document` component for you. - -```jsx -// pages/_document.js -import { Html, Head, Main, NextScript } from 'next/document' - -export default function Document() { - return ( - - - -
- - - - ) -} -``` - -### `next/app` - -The usage of `_app.js` is the same as [Custom App](/docs/advanced-features/custom-app). Using custom app as server component such as `_app.server.js` is not recommended, to keep align with non server components apps for client specific things like global CSS imports. - -### Routing - -Both basic routes with path and queries and dynamic routes are supported. If you need to access the router in server components(`.server.js`), they will receive `router` instance as a prop so that you can directly access them without using the `useRouter()` hook. - -```jsx -// pages/index.server.js - -export default function Index({ router }) { - // You can access routing information by `router.pathname`, etc. - return 'hello' -} -``` - -### Unsupported Next.js APIs - -While RSC and SSR streaming are still in the alpha stage, not all Next.js APIs are supported. The following Next.js APIs have limited functionality within Server Components. React 18 use without SSR streaming is not affected. - -#### React internals - -Most React hooks, such as `useContext`, `useState`, `useReducer`, `useEffect` and `useLayoutEffect`, are not supported as of today since server components are executed per request and aren't stateful. - -#### Data Fetching & Styling - -Like streaming SSR, styling and data fetching within `Suspense` on the server side are not well supported. We're still working on them. - -Page level exported methods like `getInitialProps`, `getStaticProps` and `getStaticPaths` are not supported. - -#### `next/head` and I18n - -We are still working on support for these features. +We will be providing more updates about Server Components usage in Next.js soon. From 3851d9065fd2cdf4b3dbef4a11667e4a224a5113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 12 Sep 2022 13:13:09 +0200 Subject: [PATCH 3/6] docs(middleware): fix broken link --- docs/api-reference/next/server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-reference/next/server.md b/docs/api-reference/next/server.md index e2f5c8e6fca0..5e83b10835e2 100644 --- a/docs/api-reference/next/server.md +++ b/docs/api-reference/next/server.md @@ -10,7 +10,7 @@ description: Learn about the server-only helpers for Middleware and Edge API Rou The `NextRequest` object is an extension of the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface, with the following added methods and properties: -- `cookies` - A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) with cookies from the `Request`. See [Using cookies in Edge Middleware](#using-cookies-in-edge-middleware) +- `cookies` - A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) with cookies from the `Request`. See [Using cookies in Middleware](/docs/advanced-features/middleware#using-cookies) - `nextUrl`: Includes an extended, parsed, URL object that gives you access to Next.js specific properties such as `pathname`, `basePath`, `trailingSlash` and `i18n`. Includes the following properties: - `basePath` (`string`) - `buildId` (`string || undefined`) From f92a4cee8ab449267c8133fd196af78c964995da Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 12 Sep 2022 14:45:37 +0200 Subject: [PATCH 4/6] Add failing case for location throw (#40445) Found that the cause was that `React.useId()` returns the same value across requests whereas it was being used to create a unique key. On further inspection that code could be removed altogether as `id` was not used client-side and the `rscCache` map is no longer needed as the flight instance is already being created per request so it can live in the context of the request execution. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have 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 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.md#adding-examples) --- packages/next/client/app-index.tsx | 8 +- packages/next/server/app-render.tsx | 101 +++++++++--------- .../app/loading-bug/[categorySlug]/loading.js | 3 + .../loading-bug/[categorySlug]/page.server.js | 15 +++ test/e2e/app-dir/index.test.ts | 15 +++ 5 files changed, 88 insertions(+), 54 deletions(-) create mode 100644 test/e2e/app-dir/app/app/loading-bug/[categorySlug]/loading.js create mode 100644 test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.server.js diff --git a/packages/next/client/app-index.tsx b/packages/next/client/app-index.tsx index a1631e60685a..84fa2cb088d5 100644 --- a/packages/next/client/app-index.tsx +++ b/packages/next/client/app-index.tsx @@ -56,7 +56,9 @@ let initialServerDataWriter: ReadableStreamDefaultController | undefined = let initialServerDataLoaded = false let initialServerDataFlushed = false -function nextServerDataCallback(seg: [number, string, string]) { +function nextServerDataCallback( + seg: [isBootStrap: 0] | [isNotBootstrap: 1, responsePartial: string] +): void { if (seg[0] === 0) { initialServerDataBuffer = [] } else { @@ -64,9 +66,9 @@ function nextServerDataCallback(seg: [number, string, string]) { throw new Error('Unexpected server data: missing bootstrap script.') if (initialServerDataWriter) { - initialServerDataWriter.enqueue(encoder.encode(seg[2])) + initialServerDataWriter.enqueue(encoder.encode(seg[1])) } else { - initialServerDataBuffer.push(seg[2]) + initialServerDataBuffer.push(seg[1]) } } } diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 7fe57797fb4c..c24206ba2420 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -54,8 +54,6 @@ function interopDefault(mod: any) { return mod.default || mod } -const rscCache = new Map() - // Shadowing check does not work with TypeScript enums // eslint-disable-next-line no-shadow const enum RecordStatus { @@ -134,57 +132,59 @@ function preloadDataFetchingRecord( */ function useFlightResponse( writable: WritableStream, - cachePrefix: string, req: ReadableStream, serverComponentManifest: any, + flightResponseRef: { + current: ReturnType | null + }, nonce?: string ) { - 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 - // We only attach CSS chunks to the inlined data. - const forwardReader = forwardStream.getReader() - const writer = writable.getWriter() - const startScriptTag = nonce - ? `` - ) + if (flightResponseRef.current) { + return flightResponseRef.current + } + + const [renderStream, forwardStream] = readableStreamTee(req) + flightResponseRef.current = createFromReadableStream(renderStream, { + moduleMap: serverComponentManifest.__ssr_module_mapping__, + }) + + let bootstrapped = false + // We only attach CSS chunks to the inlined data. + const forwardReader = forwardStream.getReader() + const writer = writable.getWriter() + const startScriptTag = nonce + ? `` ) - } - if (done) { - rscCache.delete(id) - writer.close() - } else { - const responsePartial = decodeText(value) - const scripts = `${startScriptTag}(self.__next_s=self.__next_s||[]).push(${htmlEscapeJsonString( - JSON.stringify([1, id, responsePartial]) - )})` - - writer.write(encodeText(scripts)) - process() - } - }) - } - process() + ) + } + if (done) { + flightResponseRef.current = null + writer.close() + } else { + const responsePartial = decodeText(value) + const scripts = `${startScriptTag}(self.__next_s=self.__next_s||[]).push(${htmlEscapeJsonString( + JSON.stringify([1, responsePartial]) + )})` + + writer.write(encodeText(scripts)) + process() + } + }) } - return entry + process() + + return flightResponseRef.current } /** @@ -200,12 +200,10 @@ function createServerComponentRenderer( } }, { - cachePrefix, transformStream, serverComponentManifest, serverContexts, }: { - cachePrefix: string transformStream: TransformStream serverComponentManifest: NonNullable serverContexts: Array< @@ -240,14 +238,16 @@ function createServerComponentRenderer( return RSCStream } + const flightResponseRef = { current: null } + const writable = transformStream.writable return function ServerComponentWrapper() { const reqStream = createRSCStream() const response = useFlightResponse( writable, - cachePrefix, reqStream, serverComponentManifest, + flightResponseRef, nonce ) return response.readRoot() @@ -1110,7 +1110,6 @@ export async function renderToHTMLOrFlight( }, ComponentMod, { - cachePrefix: initialCanonicalUrl, transformStream: serverComponentsInlinedTransformStream, serverComponentManifest, serverContexts, diff --git a/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/loading.js b/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/loading.js new file mode 100644 index 000000000000..f1ca6af34151 --- /dev/null +++ b/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/loading.js @@ -0,0 +1,3 @@ +export default function Loading() { + return

Loading...

+} diff --git a/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.server.js b/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.server.js new file mode 100644 index 000000000000..9352955a5043 --- /dev/null +++ b/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.server.js @@ -0,0 +1,15 @@ +// @ts-ignore +import { experimental_use as use } from 'react' + +const fetchCategory = async (categorySlug) => { + // artificial delay + await new Promise((resolve) => setTimeout(resolve, 3000)) + + return categorySlug + 'abc' +} + +export default function Page({ params }) { + const category = use(fetchCategory(params.categorySlug)) + + return
{category}
+} diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index 4cd2ec592a71..6af635e765ed 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -1419,6 +1419,21 @@ describe('app dir', () => { expect(errors).toInclude('Error during SSR') }) }) + + describe('known bugs', () => { + it('should not share flight data between requests', async () => { + const fetches = await Promise.all( + [...new Array(5)].map(() => + renderViaHTTP(next.url, '/loading-bug/electronics') + ) + ) + + for (const text of fetches) { + const $ = cheerio.load(text) + expect($('#category-id').text()).toBe('electronicsabc') + } + }) + }) } describe('without assetPrefix', () => { From c7d3f9d413a909672e0946b5223f230a935f0e75 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 12 Sep 2022 15:15:18 +0200 Subject: [PATCH 5/6] Drop legacy RSC handling in client for pages (#40472) Remove the unused RSC handling for pages in both server router and client side in favor of using RSC in app dir --- packages/next/build/webpack-config.ts | 26 +-- packages/next/client/index.tsx | 151 +----------------- packages/next/server/dev/next-dev-server.ts | 1 - packages/next/server/render.tsx | 15 -- packages/next/shared/lib/constants.ts | 1 - packages/next/shared/lib/router/router.ts | 45 +----- packages/next/shared/lib/utils.ts | 2 - .../react-refresh-utils/internal/helpers.ts | 1 - 8 files changed, 20 insertions(+), 222 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 452a2afdc307..a431bc0a55c7 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 a4d62af4256a..7324cccd7d90 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 11cbc454c0cd..d3176c140dd6 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 c352429e7bf6..2ec63d476a1b 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 856c638eac75..c334cf38fd34 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 5fab0820e1b6..1ad9a039b42a 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 76b9eee3a520..67c486a83d1e 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 7428b73196d1..9ff15e9808c4 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' ) From 735575708ab2dad926f1b4e3064952cdaf4f76e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 12 Sep 2022 17:43:53 +0200 Subject: [PATCH 6/6] docs: move `swcMinify: true` out of "Experimental features" section (#40394) See the blog post: https://nextjs.org/blog/next-12-3##swc-minifier-stable ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have 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 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.md#adding-examples) Co-authored-by: JJ Kasper --- docs/advanced-features/compiler.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/advanced-features/compiler.md b/docs/advanced-features/compiler.md index 3f2bb69033ef..6708b07d693d 100644 --- a/docs/advanced-features/compiler.md +++ b/docs/advanced-features/compiler.md @@ -9,6 +9,7 @@ description: Learn about the Next.js Compiler, written in Rust, which transforms | Version | Changes | | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `v12.3.0` | SWC Minifier [stable](https://nextjs.org/blog/next-12-3#swc-minifier-stable). | | `v12.2.0` | [SWC Plugins](#swc-plugins-Experimental) experimental support added. | | `v12.1.0` | Added support for Styled Components, Jest, Relay, Remove React Properties, Legacy Decorators, Remove Console, and jsxImportSource. | | `v12.0.0` | Next.js Compiler [introduced](https://nextjs.org/blog/next-12). | @@ -238,8 +239,6 @@ module.exports = { Only `importMap` in `@emotion/babel-plugin` is not supported for now. -## Experimental Features - ### Minification You can opt-in to using the Next.js compiler for minification. This is 7x faster than Terser. @@ -254,6 +253,8 @@ module.exports = { If you have feedback about `swcMinify`, please share it on the [feedback discussion](https://github.com/vercel/next.js/discussions/30237). +## Experimental Features + ### Minifier debug options While the minifier is experimental, we are making the following options available for debugging purposes. They will not be available once the minifier is made stable.