From 155aaa315324e08ef14d9decf780bd5ae701cad8 Mon Sep 17 00:00:00 2001 From: Rishabh Poddar Date: Sat, 6 Nov 2021 10:58:38 +0530 Subject: [PATCH 1/5] Optimise bundle size for with-supertokens example (#31040) This PR optimises the bundle size sent to the frontend for nextJS apps by lazy loading the SuperTokens backend SDK only when it's needed. --- examples/with-supertokens/config/appInfo.js | 15 ++++++++ ...{supertokensConfig.js => backendConfig.js} | 38 +------------------ .../with-supertokens/config/frontendConfig.js | 23 +++++++++++ examples/with-supertokens/pages/_app.js | 11 ++++-- .../pages/api/auth/[[...path]].js | 4 +- examples/with-supertokens/pages/api/user.js | 4 +- 6 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 examples/with-supertokens/config/appInfo.js rename examples/with-supertokens/config/{supertokensConfig.js => backendConfig.js} (59%) create mode 100644 examples/with-supertokens/config/frontendConfig.js diff --git a/examples/with-supertokens/config/appInfo.js b/examples/with-supertokens/config/appInfo.js new file mode 100644 index 0000000000000..081b4c89d991e --- /dev/null +++ b/examples/with-supertokens/config/appInfo.js @@ -0,0 +1,15 @@ +const port = process.env.APP_PORT || 3000 + +const apiBasePath = '/api/auth/' + +export const websiteDomain = + process.env.APP_URL || + process.env.NEXT_PUBLIC_APP_URL || + `http://localhost:${port}` + +export const appInfo = { + appName: 'SuperTokens Demo App', + websiteDomain, + apiDomain: websiteDomain, + apiBasePath, +} diff --git a/examples/with-supertokens/config/supertokensConfig.js b/examples/with-supertokens/config/backendConfig.js similarity index 59% rename from examples/with-supertokens/config/supertokensConfig.js rename to examples/with-supertokens/config/backendConfig.js index d56d0447ee9e6..20b91ff544ac5 100644 --- a/examples/with-supertokens/config/supertokensConfig.js +++ b/examples/with-supertokens/config/backendConfig.js @@ -1,22 +1,6 @@ import ThirdPartyEmailPasswordNode from 'supertokens-node/recipe/thirdpartyemailpassword' import SessionNode from 'supertokens-node/recipe/session' - -import ThirdPartyEmailPasswordReact from 'supertokens-auth-react/recipe/thirdpartyemailpassword' -import SessionReact from 'supertokens-auth-react/recipe/session' - -const port = process.env.APP_PORT || 3000 -const websiteDomain = - process.env.APP_URL || - process.env.NEXT_PUBLIC_APP_URL || - `http://localhost:${port}` -const apiBasePath = '/api/auth/' - -let appInfo = { - appName: 'SuperTokens Demo App', - websiteDomain, - apiDomain: websiteDomain, - apiBasePath, -} +import { appInfo } from './appInfo' export let backendConfig = () => { return { @@ -50,23 +34,3 @@ export let backendConfig = () => { isInServerlessEnv: true, } } - -export let frontendConfig = () => { - return { - appInfo, - recipeList: [ - ThirdPartyEmailPasswordReact.init({ - emailVerificationFeature: { - mode: 'REQUIRED', - }, - signInAndUpFeature: { - providers: [ - ThirdPartyEmailPasswordReact.Google.init(), - ThirdPartyEmailPasswordReact.Github.init(), - ], - }, - }), - SessionReact.init(), - ], - } -} diff --git a/examples/with-supertokens/config/frontendConfig.js b/examples/with-supertokens/config/frontendConfig.js new file mode 100644 index 0000000000000..7ddfe54ed931e --- /dev/null +++ b/examples/with-supertokens/config/frontendConfig.js @@ -0,0 +1,23 @@ +import ThirdPartyEmailPasswordReact from 'supertokens-auth-react/recipe/thirdpartyemailpassword' +import SessionReact from 'supertokens-auth-react/recipe/session' +import { appInfo } from './appInfo' + +export let frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyEmailPasswordReact.init({ + emailVerificationFeature: { + mode: 'REQUIRED', + }, + signInAndUpFeature: { + providers: [ + ThirdPartyEmailPasswordReact.Google.init(), + ThirdPartyEmailPasswordReact.Github.init(), + ], + }, + }), + SessionReact.init(), + ], + } +} diff --git a/examples/with-supertokens/pages/_app.js b/examples/with-supertokens/pages/_app.js index 509412d98d032..c44a9b2670e9e 100644 --- a/examples/with-supertokens/pages/_app.js +++ b/examples/with-supertokens/pages/_app.js @@ -2,15 +2,20 @@ import '../styles/globals.css' import React from 'react' import { useEffect } from 'react' import SuperTokensReact from 'supertokens-auth-react' -import * as SuperTokensConfig from '../config/supertokensConfig' +import * as SuperTokensConfig from '../config/frontendConfig' import Session from 'supertokens-auth-react/recipe/session' -import SuperTokensNode from 'supertokens-node' import { redirectToAuth } from 'supertokens-auth-react/recipe/thirdpartyemailpassword' +async function initNode() { + const supertokensNode = await import('supertokens-node') + const { backendConfig } = await import('../config/backendConfig') + supertokensNode.init(backendConfig()) +} + if (typeof window !== 'undefined') { SuperTokensReact.init(SuperTokensConfig.frontendConfig()) } else { - SuperTokensNode.init(SuperTokensConfig.backendConfig()) + initNode().catch(console.error) } function MyApp({ Component, pageProps }) { diff --git a/examples/with-supertokens/pages/api/auth/[[...path]].js b/examples/with-supertokens/pages/api/auth/[[...path]].js index 323c685218e98..903812d578257 100644 --- a/examples/with-supertokens/pages/api/auth/[[...path]].js +++ b/examples/with-supertokens/pages/api/auth/[[...path]].js @@ -2,9 +2,9 @@ import { superTokensNextWrapper } from 'supertokens-node/nextjs' import supertokens from 'supertokens-node' import { middleware } from 'supertokens-node/framework/express' -import * as SuperTokensConfig from '../../../config/supertokensConfig' +import { backendConfig } from '../../../config/backendConfig' -supertokens.init(SuperTokensConfig.backendConfig()) +supertokens.init(backendConfig()) export default async function superTokens(req, res) { await superTokensNextWrapper( diff --git a/examples/with-supertokens/pages/api/user.js b/examples/with-supertokens/pages/api/user.js index 2d47b0c6834db..f76a139d170b8 100644 --- a/examples/with-supertokens/pages/api/user.js +++ b/examples/with-supertokens/pages/api/user.js @@ -1,9 +1,9 @@ import { superTokensNextWrapper } from 'supertokens-node/nextjs' import { verifySession } from 'supertokens-node/recipe/session/framework/express' import supertokens from 'supertokens-node' -import * as SuperTokensConfig from '../../config/supertokensConfig' +import { backendConfig } from '../../config/backendConfig' -supertokens.init(SuperTokensConfig.backendConfig()) +supertokens.init(backendConfig()) export default async function user(req, res) { await superTokensNextWrapper( From 9e339fdd1ffa485653e1986def90b7c70b0d5ba6 Mon Sep 17 00:00:00 2001 From: chemicalkosek Date: Sat, 6 Nov 2021 06:55:55 +0100 Subject: [PATCH 2/5] Update remark and remark-html dependencies (#31051) That example errors out on ``` TypeError: (0 , remark__WEBPACK_IMPORTED_MODULE_0__.remark) is not a function ``` Updating those dependencies fixes the problem. ## 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 `yarn lint` --- examples/cms-strapi/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cms-strapi/package.json b/examples/cms-strapi/package.json index 25960e6c07321..147d835ceb423 100644 --- a/examples/cms-strapi/package.json +++ b/examples/cms-strapi/package.json @@ -11,8 +11,8 @@ "next": "latest", "react": "^17.0.2", "react-dom": "^17.0.2", - "remark": "12.0.0", - "remark-html": "11.0.2" + "remark": "^14.0.1", + "remark-html": "^15.0.0" }, "devDependencies": { "autoprefixer": "10.2.6", From e029ace5edd5a46544c2817e95964a8358133ea9 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sat, 6 Nov 2021 12:27:40 +0100 Subject: [PATCH 3/5] Fix custom 404 page when concurrentFeatures is enabled (#31059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit x-ref: https://github.com/vercel/next.js/issues/30424#issuecomment-955615781 This fix the custom 404 is not rendering properly and can’t be built in web runtime when `concurrentFeatures` is enabled. We force 404 page to be rendered outside of middleware ssr. Then it could be the real fallback 404 page in next-server when any routes is not macthed. Will check 500 related after #31057 is landed. ## Bug - [x] Related to #30989 - [x] Integration tests added --- packages/next/build/entries.ts | 18 +++----- packages/next/build/index.ts | 20 +++++---- packages/next/build/utils.ts | 9 ++++ .../next-middleware-ssr-loader/index.ts | 18 ++++---- .../webpack/plugins/middleware-plugin.ts | 5 +-- packages/next/server/dev/hot-reloader.ts | 42 ++++++++++++------- packages/next/server/dev/next-dev-server.ts | 7 +--- .../server/dev/on-demand-entry-handler.ts | 14 +++++-- .../app/pages/404.js | 3 ++ .../test/index.test.js | 10 +++++ 10 files changed, 90 insertions(+), 56 deletions(-) create mode 100644 test/integration/react-streaming-and-server-components/app/pages/404.js diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index c5d58c46cd10e..be2f0b3e8236e 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -12,7 +12,7 @@ import { ClientPagesLoaderOptions } from './webpack/loaders/next-client-pages-lo import { ServerlessLoaderQuery } from './webpack/loaders/next-serverless-loader' import { LoadedEnvFiles } from '@next/env' import { NextConfigComplete } from '../server/config-shared' -import { isFlightPage } from './utils' +import { isCustomErrorPage, isFlightPage, isReservedPage } from './utils' import { ssrEntries } from './webpack/plugins/middleware-plugin' import type { webpack5 } from 'next/dist/compiled/webpack/webpack' import { MIDDLEWARE_SSR_RUNTIME_WEBPACK } from '../shared/lib/constants' @@ -136,7 +136,10 @@ export function createEntrypoints( const serverBundlePath = posix.join('pages', bundleFile) const isLikeServerless = isTargetLikeServerless(target) + const isReserved = isReservedPage(page) + const isCustomError = isCustomErrorPage(page) const isFlight = isFlightPage(config, absolutePagePath) + const webServerRuntime = !!config.experimental.concurrentFeatures if (page.match(MIDDLEWARE_ROUTE)) { @@ -151,11 +154,7 @@ export function createEntrypoints( return } - if ( - webServerRuntime && - !(page === '/_app' || page === '/_error' || page === '/_document') && - !isApiRoute - ) { + if (webServerRuntime && !isReserved && !isCustomError && !isApiRoute) { ssrEntries.set(clientBundlePath, { requireFlightManifest: isFlight }) serverWeb[serverBundlePath] = finalizeEntrypoint({ name: '[name].js', @@ -184,12 +183,7 @@ export function createEntrypoints( serverlessLoaderOptions )}!` } else if (isApiRoute || target === 'server') { - if ( - !webServerRuntime || - page === '/_document' || - page === '/_app' || - page === '/_error' - ) { + if (!webServerRuntime || isReserved || isCustomError) { server[serverBundlePath] = [absolutePagePath] } } else if ( diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 19961b9826be1..aaff62ec7fca2 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -91,6 +91,8 @@ import { printTreeView, getCssFilePaths, getUnresolvedModuleFromError, + isReservedPage, + isCustomErrorPage, } from './utils' import getBaseWebpackConfig from './webpack-config' import { PagesManifest } from './webpack/plugins/pages-manifest-plugin' @@ -102,8 +104,6 @@ import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin' import { MiddlewareManifest } from './webpack/plugins/middleware-plugin' import type { webpack5 as webpack } from 'next/dist/compiled/webpack/webpack' -const RESERVED_PAGE = /^\/(_app|_error|_document|api(\/|$))/ - export type SsgRoute = { initialRevalidateSeconds: number | false srcRoute: string | null @@ -465,7 +465,7 @@ export default async function build( (page) => !isDynamicRoute(page) && !page.match(MIDDLEWARE_ROUTE) && - !page.match(RESERVED_PAGE) + !isReservedPage(page) ) .map(pageToRoute), dataRoutes: [], @@ -916,7 +916,7 @@ export default async function build( if ( !isMiddlewareRoute && - !page.match(RESERVED_PAGE) && + !isReservedPage(page) && !hasConcurrentFeatures ) { try { @@ -1029,7 +1029,8 @@ export default async function build( isWebSsr: hasConcurrentFeatures && !isMiddlewareRoute && - !page.match(RESERVED_PAGE), + !isReservedPage(page) && + !isCustomErrorPage(page), isHybridAmp, ssgPageRoutes, initialRevalidateSeconds: false, @@ -1318,7 +1319,9 @@ export default async function build( // Since custom _app.js can wrap the 404 page we have to opt-out of static optimization if it has getInitialProps // Only export the static 404 when there is no /_error present const useStatic404 = - !customAppGetInitialProps && (!hasNonStaticErrorPage || hasPages404) + !hasConcurrentFeatures && + !customAppGetInitialProps && + (!hasNonStaticErrorPage || hasPages404) if (invalidPages.size > 0) { const err = new Error( @@ -1383,7 +1386,10 @@ export default async function build( const combinedPages = [...staticPages, ...ssgPages] - if (combinedPages.length > 0 || useStatic404 || useDefaultStatic500) { + if ( + !hasConcurrentFeatures && + (combinedPages.length > 0 || useStatic404 || useDefaultStatic500) + ) { const staticGenerationSpan = nextBuildSpan.traceChild('static-generation') await staticGenerationSpan.traceAsyncFn(async () => { detectConflictingPaths( diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 3b87294fd8b09..54a4894401122 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -37,6 +37,7 @@ import { NextConfigComplete } from '../server/config-shared' import isError from '../lib/is-error' const { builtinModules } = require('module') +const RESERVED_PAGE = /^\/(_app|_error|_document|api(\/|$))/ const fileGzipStats: { [k: string]: Promise | undefined } = {} const fsStatGzip = (file: string) => { const cached = fileGzipStats[file] @@ -1144,3 +1145,11 @@ export function getUnresolvedModuleFromError( const [, moduleName] = error.match(moduleErrorRegex) || [] return builtinModules.find((item: string) => item === moduleName) } + +export function isReservedPage(page: string) { + return RESERVED_PAGE.test(page) +} + +export function isCustomErrorPage(page: string) { + return page === '/404' || page === '/500' +} diff --git a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts index 07f6dfd086182..b0ee97c93888f 100644 --- a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts @@ -96,13 +96,13 @@ export default async function middlewareRSCLoader(this: any) { createElement(FlightWrapper, props) ) }` - : ` - const Component = Page` + : `const Component = Page` } async function render(request) { const url = request.nextUrl - const query = Object.fromEntries(url.searchParams) + const { pathname, searchParams } = url + const query = Object.fromEntries(searchParams) // Preflight request if (request.method === 'HEAD') { @@ -122,9 +122,9 @@ export default async function middlewareRSCLoader(this: any) { wrapReadable( renderFlight({ router: { - route: url.pathname, - asPath: url.pathname, - pathname: url.pathname, + route: pathname, + asPath: pathname, + pathname: pathname, query, } }) @@ -165,9 +165,9 @@ export default async function middlewareRSCLoader(this: any) { try { const result = await renderToHTML( - { url: url.pathname }, + { url: pathname }, {}, - url.pathname, + pathname, query, renderOpts ) @@ -177,7 +177,7 @@ export default async function middlewareRSCLoader(this: any) { }) } catch (err) { return new Response( - (err || 'An error occurred while rendering ' + url.pathname + '.').toString(), + (err || 'An error occurred while rendering ' + pathname + '.').toString(), { status: 500, headers: { 'x-middleware-ssr': '1' } diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 579cfb8a230da..a42354f27a04b 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -100,9 +100,8 @@ export default class MiddlewarePlugin { ) middlewareManifest.clientInfo = middlewareManifest.sortedMiddleware.map( (key) => { - const ssrEntryInfo = ssrEntries.get( - middlewareManifest.middleware[key].name - ) + const middleware = middlewareManifest.middleware[key] + const ssrEntryInfo = ssrEntries.get(middleware.name) return [key, !!ssrEntryInfo] } ) diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index db7c7a700ad47..7a0164864c2c5 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -29,7 +29,12 @@ import { fileExists } from '../../lib/file-exists' import { ClientPagesLoaderOptions } from '../../build/webpack/loaders/next-client-pages-loader' import { ssrEntries } from '../../build/webpack/plugins/middleware-plugin' import { stringify } from 'querystring' -import { difference, isFlightPage } from '../../build/utils' +import { + difference, + isCustomErrorPage, + isFlightPage, + isReservedPage, +} from '../../build/utils' import { NextConfigComplete } from '../config-shared' import { CustomRoutes } from '../../lib/load-custom-routes' import { DecodeError } from '../../shared/lib/utils' @@ -441,11 +446,18 @@ export default class HotReloader { await Promise.all( Object.keys(entries).map(async (pageKey) => { const isClientKey = pageKey.startsWith('client') + const isServerWebKey = pageKey.startsWith('server-web') if (isClientKey !== isClientCompilation) return + if (isServerWebKey !== isServerWebCompilation) return const page = pageKey.slice( - isClientKey ? 'client'.length : 'server'.length + isClientKey + ? 'client'.length + : isServerWebKey + ? 'server-web'.length + : 'server'.length ) - const isMiddleware = page.match(MIDDLEWARE_ROUTE) + const isMiddleware = !!page.match(MIDDLEWARE_ROUTE) + if (isClientCompilation && page.match(API_ROUTE) && !isMiddleware) { return } @@ -464,11 +476,18 @@ export default class HotReloader { return } + const isCustomError = isCustomErrorPage(page) + const isReserved = isReservedPage(page) const isServerComponent = this.hasServerComponents && isFlightPage(this.config, absolutePagePath) - if (isServerCompilation && this.webServerRuntime && !isApiRoute) { + if ( + isServerCompilation && + this.webServerRuntime && + !isApiRoute && + !isCustomError + ) { return } @@ -496,22 +515,13 @@ export default class HotReloader { ssrEntries.set(bundlePath, { requireFlightManifest: true }) } else if ( this.webServerRuntime && - !( - page === '/_app' || - page === '/_error' || - page === '/_document' - ) + !isReserved && + !isCustomError ) { ssrEntries.set(bundlePath, { requireFlightManifest: false }) } } else if (isServerWebCompilation) { - if ( - !( - page === '/_app' || - page === '/_error' || - page === '/_document' - ) - ) { + if (!isReserved) { entrypoints[bundlePath] = finalizeEntrypoint({ name: '[name].js', value: `next-middleware-ssr-loader?${stringify({ diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 03ac4dec26d56..339d5e183bfce 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -59,6 +59,7 @@ import isError from '../../lib/is-error' import { getMiddlewareRegex } from '../../shared/lib/router/utils/get-middleware-regex' import type { FetchEventResult } from '../web/types' import type { ParsedNextUrl } from '../../shared/lib/router/utils/parse-next-url' +import { isCustomErrorPage, isReservedPage } from '../../build/utils' // Load ReactDevOverlay only when needed let ReactDevOverlayImpl: React.FunctionComponent @@ -272,11 +273,7 @@ export default class DevServer extends Server { ssrMiddleware.add(pageName) } else if ( isWebServerRuntime && - !( - pageName === '/_app' || - pageName === '/_error' || - pageName === '/_document' - ) + !(isReservedPage(pageName) || isCustomErrorPage(pageName)) ) { routedMiddleware.push(pageName) ssrMiddleware.add(pageName) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index 493222349d0f1..0db64b4fef23a 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -9,12 +9,13 @@ import { API_ROUTE, MIDDLEWARE_ROUTE } from '../../lib/constants' import { reportTrigger } from '../../build/output' import type ws from 'ws' import { NextConfigComplete } from '../config-shared' +import { isCustomErrorPage } from '../../build/utils' export const ADDED = Symbol('added') export const BUILDING = Symbol('building') export const BUILT = Symbol('built') -export let entries: { +export const entries: { [page: string]: { bundlePath: string absolutePagePath: string @@ -204,6 +205,7 @@ export default function onDemandEntryHandler( const isMiddleware = normalizedPage.match(MIDDLEWARE_ROUTE) const isApiRoute = normalizedPage.match(API_ROUTE) && !isMiddleware const isServerWeb = !!nextConfig.experimental.concurrentFeatures + const isCustomError = isCustomErrorPage(page) let entriesChanged = false const addPageEntry = (type: 'client' | 'server' | 'server-web') => { @@ -242,20 +244,24 @@ export default function onDemandEntryHandler( }) } + const isClientOrMiddleware = clientOnly || isMiddleware + const promise = isApiRoute ? addPageEntry('server') - : clientOnly || isMiddleware + : isClientOrMiddleware ? addPageEntry('client') : Promise.all([ addPageEntry('client'), - addPageEntry(isServerWeb ? 'server-web' : 'server'), + addPageEntry( + isServerWeb && !isCustomError ? 'server-web' : 'server' + ), ]) if (entriesChanged) { reportTrigger( isApiRoute ? `${normalizedPage} (server only)` - : clientOnly || isMiddleware + : isClientOrMiddleware ? `${normalizedPage} (client only)` : normalizedPage ) diff --git a/test/integration/react-streaming-and-server-components/app/pages/404.js b/test/integration/react-streaming-and-server-components/app/pages/404.js new file mode 100644 index 0000000000000..92471c97e449d --- /dev/null +++ b/test/integration/react-streaming-and-server-components/app/pages/404.js @@ -0,0 +1,3 @@ +export default function Page404() { + return 'custom-404-page' +} diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index c8f5e4999bb21..d8dcfe043d2ea 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -140,6 +140,7 @@ describe('concurrentFeatures - prod', () => { ]) { expect(content.clientInfo).toContainEqual(item) } + expect(content.clientInfo).not.toContainEqual([['/404', true]]) }) it('should support React.lazy and dynamic imports', async () => { @@ -215,11 +216,20 @@ async function runBasicTests(context) { '/routes/dynamic2' ) + const path404HTML = await renderViaHTTP(context.appPort, '/404') + const pathNotFoundHTML = await renderViaHTTP( + context.appPort, + '/this-is-not-found' + ) + expect(homeHTML).toContain('thisistheindexpage.server') expect(homeHTML).toContain('foo.client') expect(dynamicRouteHTML1).toContain('[pid]') expect(dynamicRouteHTML2).toContain('[pid]') + + expect(path404HTML).toContain('custom-404-page') + expect(pathNotFoundHTML).toContain('custom-404-page') }) it('should suspense next/link on server side', async () => { From 5e185fc5da227801d3f12724be3577f4a719aa69 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Sat, 6 Nov 2021 13:08:03 +0100 Subject: [PATCH 4/5] Upgrade React alpha and experimental dependencies to latest (#31039) Ensuring that our tests are not broken with latest React changes. ## 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 `yarn lint` --- package.json | 4 +-- ...bpack-writer.browser.development.server.js | 26 +++++++------- ...ck-writer.browser.production.min.server.js | 35 ++++++++++--------- .../react-server-dom-webpack.development.js | 6 ++-- ...react-server-dom-webpack.production.min.js | 20 +++++------ packages/next/package.json | 2 +- yarn.lock | 34 +++++++++--------- 7 files changed, 62 insertions(+), 65 deletions(-) diff --git a/package.json b/package.json index 52d82b750b8df..7e5cebe0b4117 100644 --- a/package.json +++ b/package.json @@ -139,9 +139,9 @@ "pretty-ms": "7.0.0", "random-seed": "0.3.0", "react": "17.0.2", - "react-18": "npm:react@18.0.0-alpha-3c4c1c470-20211021", + "react-18": "npm:react@18.0.0-alpha-13455d26d-20211104", "react-dom": "17.0.2", - "react-dom-18": "npm:react-dom@18.0.0-alpha-3c4c1c470-20211021", + "react-dom-18": "npm:react-dom@18.0.0-alpha-13455d26d-20211104", "react-ssr-prepass": "1.0.8", "release": "6.3.0", "request-promise-core": "1.1.2", diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js index b8f681839abe0..6f4c459c3a7ed 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js @@ -144,7 +144,6 @@ var REACT_SUSPENSE_LIST_TYPE = 0xead8; var REACT_MEMO_TYPE = 0xead3; var REACT_LAZY_TYPE = 0xead4; var REACT_SCOPE_TYPE = 0xead7; -var REACT_OPAQUE_ID_TYPE = 0xeae0; var REACT_DEBUG_TRACING_MODE_TYPE = 0xeae1; var REACT_OFFSCREEN_TYPE = 0xeae2; var REACT_LEGACY_HIDDEN_TYPE = 0xeae3; @@ -165,7 +164,6 @@ if (typeof Symbol === 'function' && Symbol.for) { REACT_MEMO_TYPE = symbolFor('react.memo'); REACT_LAZY_TYPE = symbolFor('react.lazy'); REACT_SCOPE_TYPE = symbolFor('react.scope'); - REACT_OPAQUE_ID_TYPE = symbolFor('react.opaque.id'); REACT_DEBUG_TRACING_MODE_TYPE = symbolFor('react.debug_trace_mode'); REACT_OFFSCREEN_TYPE = symbolFor('react.offscreen'); REACT_LEGACY_HIDDEN_TYPE = symbolFor('react.legacy_hidden'); @@ -219,7 +217,7 @@ function attemptResolveElement(type, key, ref, props) { // When the ref moves to the regular props object this will implicitly // throw for functions. We could probably relax it to a DEV warning for other // cases. - throw Error( 'Refs cannot be used in server components, nor passed to client components.' ); + throw new Error('Refs cannot be used in server components, nor passed to client components.'); } if (typeof type === 'function') { @@ -260,7 +258,7 @@ function attemptResolveElement(type, key, ref, props) { } } - throw Error( "Unsupported server component type: " + describeValueForErrorMessage(type) ); + throw new Error("Unsupported server component type: " + describeValueForErrorMessage(type)); } function pingSegment(request, segment) { @@ -480,7 +478,7 @@ function resolveModelToJSON(request, parent, key, value) { return '$'; case REACT_LAZY_TYPE: - throw Error( 'React Lazy Components are not yet supported on the server.' ); + throw new Error('React Lazy Components are not yet supported on the server.'); } // Resolve server components. @@ -593,9 +591,9 @@ function resolveModelToJSON(request, parent, key, value) { if (typeof value === 'function') { if (/^on[A-Z]/.test(key)) { - throw Error( 'Event handlers cannot be passed to client component props. ' + ("Remove " + describeKeyForErrorMessage(key) + " from these props if possible: " + describeObjectForErrorMessage(parent) + "\n") + 'If you need interactivity, consider converting part of this to a client component.' ); + throw new Error('Event handlers cannot be passed to client component props. ' + ("Remove " + describeKeyForErrorMessage(key) + " from these props if possible: " + describeObjectForErrorMessage(parent) + "\n") + 'If you need interactivity, consider converting part of this to a client component.'); } else { - throw Error( 'Functions cannot be passed directly to client components ' + "because they're not serializable. " + ("Remove " + describeKeyForErrorMessage(key) + " (" + (value.displayName || value.name || 'function') + ") from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent)) ); + throw new Error('Functions cannot be passed directly to client components ' + "because they're not serializable. " + ("Remove " + describeKeyForErrorMessage(key) + " (" + (value.displayName || value.name || 'function') + ") from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent))); } } @@ -611,7 +609,7 @@ function resolveModelToJSON(request, parent, key, value) { var name = value.description; if (Symbol.for(name) !== value) { - throw Error( 'Only global symbols received from Symbol.for(...) can be passed to client components. ' + ("The symbol Symbol.for(" + value.description + ") cannot be found among global symbols. ") + ("Remove " + describeKeyForErrorMessage(key) + " from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent)) ); + throw new Error('Only global symbols received from Symbol.for(...) can be passed to client components. ' + ("The symbol Symbol.for(" + value.description + ") cannot be found among global symbols. ") + ("Remove " + describeKeyForErrorMessage(key) + " from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent))); } request.pendingChunks++; @@ -623,10 +621,10 @@ function resolveModelToJSON(request, parent, key, value) { if (typeof value === 'bigint') { - throw Error( "BigInt (" + value + ") is not yet supported in client component props. " + ("Remove " + describeKeyForErrorMessage(key) + " from this object or use a plain number instead: " + describeObjectForErrorMessage(parent)) ); + throw new Error("BigInt (" + value + ") is not yet supported in client component props. " + ("Remove " + describeKeyForErrorMessage(key) + " from this object or use a plain number instead: " + describeObjectForErrorMessage(parent))); } - throw Error( "Type " + typeof value + " is not supported in client component props. " + ("Remove " + describeKeyForErrorMessage(key) + " from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent)) ); + throw new Error("Type " + typeof value + " is not supported in client component props. " + ("Remove " + describeKeyForErrorMessage(key) + " from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent))); } function reportError(request, error) { @@ -826,12 +824,12 @@ function startFlowing(request, destination) { } function unsupportedHook() { - throw Error( 'This Hook is not supported in Server Components.' ); + throw new Error('This Hook is not supported in Server Components.'); } function unsupportedRefresh() { if (!currentCache) { - throw Error( 'Refreshing the cache is not supported in Server Components.' ); + throw new Error('Refreshing the cache is not supported in Server Components.'); } } @@ -848,7 +846,7 @@ var Dispatcher = { useTransition: unsupportedHook, getCacheForType: function (resourceType) { if (!currentCache) { - throw Error( 'Reading the cache is only supported while rendering.' ); + throw new Error('Reading the cache is only supported while rendering.'); } var entry = currentCache.get(resourceType); @@ -870,7 +868,7 @@ var Dispatcher = { useLayoutEffect: unsupportedHook, useImperativeHandle: unsupportedHook, useEffect: unsupportedHook, - useOpaqueIdentifier: unsupportedHook, + useId: unsupportedHook, useMutableSource: unsupportedHook, useSyncExternalStore: unsupportedHook, useCacheRefresh: function () { diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js index 9364c335a241a..2977e6eea4d4f 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js @@ -6,20 +6,21 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict';var h=require("react");function m(a){for(var d="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c=a.length?a:a.substr(0,10)+"...");case "object":if(C(a))return"[...]";a=O(a);return"Object"===a?"{...}":a;case "function":return"function";default:return String(a)}} -function Q(a,d){if(C(a)){for(var c="[",b=0;b=a.length?a:a.substr(0,10)+"...");case "object":if(B(a))return"[...]";a=N(a);return"Object"===a?"{...}":a;case "function":return"function";default:return String(a)}} +function P(a,d){if(B(a)){for(var c="[",b=0;b Date: Sat, 6 Nov 2021 19:37:24 +0100 Subject: [PATCH 5/5] Change disabled SWC message to Log.info (#31091) Based on feedback from @flybayer, this changes the `warn - ` to `info -` for the "Disabled SWC" message. ## 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 `yarn lint` --- packages/next/build/webpack-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 71174550d9c0f..730856f88af24 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -452,7 +452,7 @@ export default async function getBaseWebpackConfig( let useSWCLoader = !babelConfigFile if (!loggedSwcDisabled && !useSWCLoader && babelConfigFile) { - Log.warn( + Log.info( `Disabled SWC as replacement for Babel because of custom Babel configuration "${path.relative( dir, babelConfigFile