Skip to content

Commit

Permalink
Ensure there is only 1 render pass in concurrent rendering with getIn…
Browse files Browse the repository at this point in the history
…itialProps in `_document` (#36352)

This PR makes sure `renderPage` calls `renderShell` in concurrent features, and `renderToString` if not.

Closes #36268, #36229.

## Bug

- [x] Related issues linked using `fixes #number`
- [x] 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`
  • Loading branch information
shuding committed Apr 21, 2022
1 parent b8f7c52 commit be9491e
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 23 deletions.
97 changes: 74 additions & 23 deletions packages/next/server/render.tsx
Expand Up @@ -395,7 +395,6 @@ const useFlightResponse = createFlightHook()

// Create the wrapper component for a Flight stream.
function createServerComponentRenderer(
AppMod: any,
ComponentMod: any,
{
cachePrefix,
Expand All @@ -415,10 +414,13 @@ function createServerComponentRenderer(
globalThis.__webpack_require__ = ComponentMod.__next_rsc__.__webpack_require__
const Component = interopDefault(ComponentMod)

function ServerComponentWrapper(props: any) {
function ServerComponentWrapper({ App, router, ...props }: any) {
const id = (React as any).useId()

const reqStream: ReadableStream<Uint8Array> = renderToReadableStream(
renderFlight(AppMod, ComponentMod, props),
<App>
<Component {...props} />
</App>,
serverComponentManifest
)

Expand Down Expand Up @@ -520,7 +522,7 @@ export async function renderToHTML(
if (isServerComponent) {
serverComponentsInlinedTransformStream = new TransformStream()
const search = urlQueryToSearchParams(query).toString()
Component = createServerComponentRenderer(AppMod, ComponentMod, {
Component = createServerComponentRenderer(ComponentMod, {
cachePrefix: pathname + (search ? `?${search}` : ''),
inlinedTransformStream: serverComponentsInlinedTransformStream,
staticTransformStream: serverComponentsPageDataTransformStream,
Expand Down Expand Up @@ -1311,11 +1313,24 @@ export async function renderToHTML(
}
}

async function documentInitialProps() {
async function documentInitialProps(
renderShell?: ({
EnhancedApp,
EnhancedComponent,
}: {
EnhancedApp?: AppType
EnhancedComponent?: NextComponentType
}) => Promise<void>
) {
const renderPage: RenderPage = (
options: ComponentsEnhancer = {}
): RenderPageResult | Promise<RenderPageResult> => {
if (ctx.err && ErrorDebug) {
// Always start rendering the shell even if there's an error.
if (renderShell) {
renderShell({})
}

const html = ReactDOMServer.renderToString(
<Body>
<ErrorDebug error={ctx.err} />
Expand All @@ -1333,6 +1348,14 @@ export async function renderToHTML(
const { App: EnhancedApp, Component: EnhancedComponent } =
enhanceComponents(options, App, Component)

if (renderShell) {
return renderShell({ EnhancedApp, EnhancedComponent }).then(() => {
// When using concurrent features, we don't have or need the full
// html so it's fine to return nothing here.
return { html: '', head }
})
}

const html = ReactDOMServer.renderToString(
<Body>
<AppContainerWithIsomorphicFiberStructure>
Expand Down Expand Up @@ -1364,20 +1387,30 @@ export async function renderToHTML(
return { docProps, documentCtx }
}

const renderContent = () => {
const renderContent = ({
EnhancedApp,
EnhancedComponent,
}: {
EnhancedApp?: AppType
EnhancedComponent?: NextComponentType
} = {}) => {
return ctx.err && ErrorDebug ? (
<Body>
<ErrorDebug error={ctx.err} />
</Body>
) : (
<Body>
<AppContainerWithIsomorphicFiberStructure>
{isServerComponent && !!AppMod.__next_rsc__ ? (
// _app.server.js is used.
<Component {...props.pageProps} />
) : (
<App {...props} Component={Component} router={router} />
)}
{isServerComponent
? React.createElement(EnhancedComponent || Component, {
App: EnhancedApp || App,
...props.pageProps,
})
: React.createElement(EnhancedApp || App, {
...props,
Component: EnhancedComponent || Component,
router,
})}
</AppContainerWithIsomorphicFiberStructure>
</Body>
)
Expand Down Expand Up @@ -1419,13 +1452,23 @@ export async function renderToHTML(
}
}
} else {
// We start rendering the shell earlier, before returning the head tags
// to `documentResult`.
const content = renderContent()
const renderStream = await renderToInitialStream({
ReactDOMServer,
element: content,
})
let renderStream: ReadableStream<Uint8Array> & {
allReady?: Promise<void> | undefined
}

const renderShell = async ({
EnhancedApp,
EnhancedComponent,
}: {
EnhancedApp?: AppType
EnhancedComponent?: NextComponentType
} = {}) => {
const content = renderContent({ EnhancedApp, EnhancedComponent })
renderStream = await renderToInitialStream({
ReactDOMServer,
element: content,
})
}

const bodyResult = async (suffix: string) => {
// this must be called inside bodyResult so appWrappers is
Expand Down Expand Up @@ -1494,10 +1537,18 @@ export async function renderToHTML(
!Document.getInitialProps
)

const documentInitialPropsRes = hasDocumentGetInitialProps
? await documentInitialProps()
: {}
if (documentInitialPropsRes === null) return null
// If it has getInitialProps, we will render the shell in `renderPage`.
// Otherwise we do it right now.
let documentInitialPropsRes:
| {}
| Awaited<ReturnType<typeof documentInitialProps>>
if (hasDocumentGetInitialProps) {
documentInitialPropsRes = await documentInitialProps(renderShell)
if (documentInitialPropsRes === null) return null
} else {
await renderShell()
documentInitialPropsRes = {}
}

const { docProps } = (documentInitialPropsRes as any) || {}
const documentElement = () => {
Expand Down
11 changes: 11 additions & 0 deletions test/development/basic/styled-components.test.ts
Expand Up @@ -73,4 +73,15 @@ describe('styled-components SWC transform', () => {
expect(html).toContain('background:transparent')
expect(html).toContain('color:white')
})

it('should only render once on the server per request', async () => {
const outputs = []
next.on('stdout', (args) => {
outputs.push(args)
})
await renderViaHTTP(next.url, '/')
expect(
outputs.filter((output) => output.trim() === '__render__').length
).toBe(1)
})
})
1 change: 1 addition & 0 deletions test/development/basic/styled-components/pages/index.js
Expand Up @@ -21,6 +21,7 @@ const Button = styled.a`
`

export default function Home() {
console.log('__render__')
return (
<div>
<Button
Expand Down
1 change: 1 addition & 0 deletions test/integration/react-18/app/pages/index.js
Expand Up @@ -5,6 +5,7 @@ export default function Index() {
if (typeof window !== 'undefined') {
window.didHydrate = true
}
console.log('__render__')
return (
<div>
<p id="react-dom-version">{ReactDOM.version}</p>
Expand Down
5 changes: 5 additions & 0 deletions test/integration/react-18/test/basics.js
Expand Up @@ -5,6 +5,11 @@ import cheerio from 'cheerio'
import { renderViaHTTP } from 'next-test-utils'

export default (context, env) => {
it('should only render once in SSR', async () => {
await renderViaHTTP(context.appPort, '/')
expect([...context.stdout.matchAll(/__render__/g)].length).toBe(1)
})

it('no warnings for image related link props', async () => {
await renderViaHTTP(context.appPort, '/')
expect(context.stderr).not.toContain('Warning: Invalid DOM property')
Expand Down
6 changes: 6 additions & 0 deletions test/lib/next-test-utils.js
Expand Up @@ -758,6 +758,10 @@ function runSuite(suiteName, context, options) {
const onStderr = (msg) => {
context.stderr += msg
}
context.stdout = ''
const onStdout = (msg) => {
context.stdout += msg
}
if (env === 'prod') {
context.appPort = await findPort()
const { stdout, stderr, code } = await nextBuild(appDir, [], {
Expand All @@ -769,11 +773,13 @@ function runSuite(suiteName, context, options) {
context.code = code
context.server = await nextStart(context.appDir, context.appPort, {
onStderr,
onStdout,
})
} else if (env === 'dev') {
context.appPort = await findPort()
context.server = await launchApp(context.appDir, context.appPort, {
onStderr,
onStdout,
})
}
})
Expand Down

1 comment on commit be9491e

@ijjk
Copy link
Member

@ijjk ijjk commented on be9491e Apr 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stats from current release

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
buildDuration 14.9s 15.3s ⚠️ +365ms
buildDurationCached 6.4s 6.3s -79ms
nodeModulesSize 86.7 MB 86.7 MB ⚠️ +26 kB
Page Load Tests Overall increase ✓
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
/ failed reqs 0 0
/ total time (seconds) 3.298 3.304 ⚠️ +0.01
/ avg req/sec 757.97 756.61 ⚠️ -1.36
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.429 1.396 -0.03
/error-in-render avg req/sec 1749.05 1790.41 +41.36
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
925.HASH.js gzip 179 B 179 B
framework-HASH.js gzip 42 kB 42 kB
main-HASH.js gzip 28.3 kB 28.5 kB ⚠️ +133 B
webpack-HASH.js gzip 1.44 kB 1.44 kB
Overall change 72 kB 72.1 kB ⚠️ +133 B
Legacy Client Bundles (polyfills)
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
_app-HASH.js gzip 1.36 kB 1.36 kB
_error-HASH.js gzip 192 B 192 B
amp-HASH.js gzip 309 B 309 B
css-HASH.js gzip 327 B 327 B
dynamic-HASH.js gzip 3.05 kB 3.04 kB -10 B
head-HASH.js gzip 351 B 351 B
hooks-HASH.js gzip 920 B 920 B
image-HASH.js gzip 5.73 kB 5.74 kB ⚠️ +12 B
index-HASH.js gzip 263 B 263 B
link-HASH.js gzip 2.36 kB 2.36 kB
routerDirect..HASH.js gzip 320 B 320 B
script-HASH.js gzip 392 B 392 B
withRouter-HASH.js gzip 319 B 319 B
85e02e95b279..7e3.css gzip 107 B 107 B
Overall change 16 kB 16 kB ⚠️ +2 B
Client Build Manifests Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
_buildManifest.js gzip 460 B 461 B ⚠️ +1 B
Overall change 460 B 461 B ⚠️ +1 B
Rendered Page Sizes Overall decrease ✓
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
index.html gzip 532 B 532 B
link.html gzip 546 B 545 B -1 B
withRouter.html gzip 527 B 526 B -1 B
Overall change 1.6 kB 1.6 kB -2 B

Diffs

Diff for _buildManifest.js
@@ -8,11 +8,11 @@ self.__BUILD_MANIFEST = {
     "static\u002Fchunks\u002Fpages\u002Fcss-f8d6ff68a6e8b080.js"
   ],
   "/dynamic": [
-    "static\u002Fchunks\u002Fpages\u002Fdynamic-f3793288d6fe105a.js"
+    "static\u002Fchunks\u002Fpages\u002Fdynamic-2fc05fc466ebee90.js"
   ],
   "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-96a5d6ed07cf5a83.js"],
   "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-9dfe734f583d4926.js"],
-  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-5597cb692d5dcc6b.js"],
+  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-9a0a6e890d6a266a.js"],
   "/link": ["static\u002Fchunks\u002Fpages\u002Flink-c605640c895e01ab.js"],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-98eb70bf22fb21da.js"
Diff for dynamic-HASH.js
@@ -633,10 +633,10 @@
       __webpack_require__
     ) {
       var __dirname = "/";
-      (() => {
+      (function() {
         "use strict";
         var e = {
-          800: e => {
+          800: function(e) {
             /*
 object-assign
 (c) Sindre Sorhus
@@ -713,7 +713,7 @@ object-assign
                   return i;
                 };
           },
-          569: (e, r, t) => {
+          569: function(e, r, t) {
             /** @license React vundefined
              * use-subscription.development.js
              *
@@ -725,7 +725,7 @@ object-assign
             if (false) {
             }
           },
-          403: (e, r, t) => {
+          403: function(e, r, t) {
             /** @license React vundefined
              * use-subscription.production.min.js
              *
@@ -775,13 +775,13 @@ object-assign
               return a;
             };
           },
-          138: (e, r, t) => {
+          138: function(e, r, t) {
             if (true) {
               e.exports = t(403);
             } else {
             }
           },
-          522: e => {
+          522: function(e) {
             e.exports = __webpack_require__(7294);
           }
         };
Diff for image-HASH.js
@@ -893,6 +893,7 @@
           onLoadingCompleteRef = _param.onLoadingCompleteRef,
           setBlurComplete = _param.setBlurComplete,
           setIntersection = _param.setIntersection,
+          onLoad = _param.onLoad,
           onError = _param.onError,
           isVisible = _param.isVisible,
           rest = _objectWithoutProperties(_param, [
@@ -914,6 +915,7 @@
             "onLoadingCompleteRef",
             "setBlurComplete",
             "setIntersection",
+            "onLoad",
             "onError",
             "isVisible"
           ]);
@@ -972,6 +974,9 @@
                     onLoadingCompleteRef,
                     setBlurComplete
                   );
+                  if (onLoad) {
+                    onLoad(event);
+                  }
                 },
                 onError: function(event) {
                   if (placeholder === "blur") {
Diff for main-HASH.js
@@ -627,7 +627,7 @@
         }
         return target;
       }
-      var version = "12.1.5";
+      var version = "12.1.6-canary.5";
       exports.version = version;
       var router;
       exports.router = router;
@@ -3533,13 +3533,15 @@
                   null,
                   /*#__PURE__*/ _react.default.createElement("style", {
                     dangerouslySetInnerHTML: {
-                      __html: "body { margin: 0 }"
+                      __html:
+                        "\n                body { margin: 0; color: #000; background: #fff; }\n                .next-error-h1 {\n                  border-right: 1px solid rgba(0, 0, 0, .3);\n                }\n                @media (prefers-color-scheme: dark) {\n                  body { color: #fff; background: #000; }\n                  .next-error-h1 {\n                    border-right: 1px solid rgba(255, 255, 255, .3);\n                  }\n                }"
                     }
                   }),
                   statusCode
                     ? /*#__PURE__*/ _react.default.createElement(
                         "h1",
                         {
+                          className: "next-error-h1",
                           style: styles.h1
                         },
                         statusCode
@@ -3578,8 +3580,6 @@
       Error.origGetInitialProps = _getInitialProps;
       var styles = {
         error: {
-          color: "#000",
-          background: "#fff",
           fontFamily:
             '-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
           height: "100vh",
@@ -3598,7 +3598,6 @@
         },
         h1: {
           display: "inline-block",
-          borderRight: "1px solid rgba(0, 0, 0,.3)",
           margin: 0,
           marginRight: "20px",
           padding: "10px 23px 10px 0",
@@ -3694,8 +3693,15 @@
         value: true
       });
       exports.escapeStringRegexp = escapeStringRegexp;
+      // regexp is based on https://github.com/sindresorhus/escape-string-regexp
+      var reHasRegExp = /[|\\{}()[\]^$+*?.-]/;
+      var reReplaceRegExp = /[|\\{}()[\]^$+*?.-]/g;
       function escapeStringRegexp(str) {
-        return str.replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&");
+        // see also: https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/escapeRegExp.js#L23
+        if (reHasRegExp.test(str)) {
+          return str.replace(reReplaceRegExp, "\\$&");
+        }
+        return str;
       } //# sourceMappingURL=escape-regexp.js.map
 
       /***/
Diff for index.html
@@ -19,7 +19,7 @@
       defer=""
     ></script>
     <script
-      src="/_next/static/chunks/main-d49dd75f151b9b34.js"
+      src="/_next/static/chunks/main-82abc31d875ace09.js"
       defer=""
     ></script>
     <script
Diff for link.html
@@ -19,7 +19,7 @@
       defer=""
     ></script>
     <script
-      src="/_next/static/chunks/main-d49dd75f151b9b34.js"
+      src="/_next/static/chunks/main-82abc31d875ace09.js"
       defer=""
     ></script>
     <script
Diff for withRouter.html
@@ -19,7 +19,7 @@
       defer=""
     ></script>
     <script
-      src="/_next/static/chunks/main-d49dd75f151b9b34.js"
+      src="/_next/static/chunks/main-82abc31d875ace09.js"
       defer=""
     ></script>
     <script

Default Build with SWC (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
buildDuration 12.1s 12.2s ⚠️ +128ms
buildDurationCached 6.2s 6.2s -39ms
nodeModulesSize 86.7 MB 86.7 MB ⚠️ +26 kB
Page Load Tests Overall increase ✓
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
/ failed reqs 0 0
/ total time (seconds) 3.304 3.232 -0.07
/ avg req/sec 756.6 773.59 +16.99
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.439 1.401 -0.04
/error-in-render avg req/sec 1737.24 1784.02 +46.78
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
925.HASH.js gzip 178 B 178 B
framework-HASH.js gzip 42.3 kB 42.3 kB
main-HASH.js gzip 28.7 kB 28.9 kB ⚠️ +212 B
webpack-HASH.js gzip 1.45 kB 1.45 kB
Overall change 72.6 kB 72.8 kB ⚠️ +212 B
Legacy Client Bundles (polyfills)
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
_app-HASH.js gzip 1.35 kB 1.35 kB
_error-HASH.js gzip 179 B 179 B
amp-HASH.js gzip 313 B 313 B
css-HASH.js gzip 325 B 325 B
dynamic-HASH.js gzip 3.03 kB 3.02 kB -2 B
head-HASH.js gzip 351 B 351 B
hooks-HASH.js gzip 921 B 921 B
image-HASH.js gzip 5.78 kB 5.79 kB ⚠️ +12 B
index-HASH.js gzip 261 B 261 B
link-HASH.js gzip 2.44 kB 2.44 kB
routerDirect..HASH.js gzip 322 B 322 B
script-HASH.js gzip 393 B 393 B
withRouter-HASH.js gzip 317 B 317 B
85e02e95b279..7e3.css gzip 107 B 107 B
Overall change 16.1 kB 16.1 kB ⚠️ +10 B
Client Build Manifests Overall decrease ✓
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
_buildManifest.js gzip 459 B 457 B -2 B
Overall change 459 B 457 B -2 B
Rendered Page Sizes Overall increase ⚠️
vercel/next.js canary v12.1.5 vercel/next.js refs/heads/canary Change
index.html gzip 530 B 532 B ⚠️ +2 B
link.html gzip 544 B 546 B ⚠️ +2 B
withRouter.html gzip 525 B 527 B ⚠️ +2 B
Overall change 1.6 kB 1.6 kB ⚠️ +6 B

Diffs

Diff for _buildManifest.js
@@ -8,11 +8,11 @@ self.__BUILD_MANIFEST = {
     "static\u002Fchunks\u002Fpages\u002Fcss-f8d6ff68a6e8b080.js"
   ],
   "/dynamic": [
-    "static\u002Fchunks\u002Fpages\u002Fdynamic-f3793288d6fe105a.js"
+    "static\u002Fchunks\u002Fpages\u002Fdynamic-2fc05fc466ebee90.js"
   ],
   "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-96a5d6ed07cf5a83.js"],
   "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-9dfe734f583d4926.js"],
-  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-5597cb692d5dcc6b.js"],
+  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-9a0a6e890d6a266a.js"],
   "/link": ["static\u002Fchunks\u002Fpages\u002Flink-c605640c895e01ab.js"],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-98eb70bf22fb21da.js"
Diff for dynamic-HASH.js
@@ -633,10 +633,10 @@
       __webpack_require__
     ) {
       var __dirname = "/";
-      (() => {
+      (function() {
         "use strict";
         var e = {
-          800: e => {
+          800: function(e) {
             /*
 object-assign
 (c) Sindre Sorhus
@@ -713,7 +713,7 @@ object-assign
                   return i;
                 };
           },
-          569: (e, r, t) => {
+          569: function(e, r, t) {
             /** @license React vundefined
              * use-subscription.development.js
              *
@@ -725,7 +725,7 @@ object-assign
             if (false) {
             }
           },
-          403: (e, r, t) => {
+          403: function(e, r, t) {
             /** @license React vundefined
              * use-subscription.production.min.js
              *
@@ -775,13 +775,13 @@ object-assign
               return a;
             };
           },
-          138: (e, r, t) => {
+          138: function(e, r, t) {
             if (true) {
               e.exports = t(403);
             } else {
             }
           },
-          522: e => {
+          522: function(e) {
             e.exports = __webpack_require__(7294);
           }
         };
Diff for image-HASH.js
@@ -893,6 +893,7 @@
           onLoadingCompleteRef = _param.onLoadingCompleteRef,
           setBlurComplete = _param.setBlurComplete,
           setIntersection = _param.setIntersection,
+          onLoad = _param.onLoad,
           onError = _param.onError,
           isVisible = _param.isVisible,
           rest = _objectWithoutProperties(_param, [
@@ -914,6 +915,7 @@
             "onLoadingCompleteRef",
             "setBlurComplete",
             "setIntersection",
+            "onLoad",
             "onError",
             "isVisible"
           ]);
@@ -972,6 +974,9 @@
                     onLoadingCompleteRef,
                     setBlurComplete
                   );
+                  if (onLoad) {
+                    onLoad(event);
+                  }
                 },
                 onError: function(event) {
                   if (placeholder === "blur") {
Diff for main-HASH.js
@@ -627,7 +627,7 @@
         }
         return target;
       }
-      var version = "12.1.5";
+      var version = "12.1.6-canary.5";
       exports.version = version;
       var router;
       exports.router = router;
@@ -3533,13 +3533,15 @@
                   null,
                   /*#__PURE__*/ _react.default.createElement("style", {
                     dangerouslySetInnerHTML: {
-                      __html: "body { margin: 0 }"
+                      __html:
+                        "\n                body { margin: 0; color: #000; background: #fff; }\n                .next-error-h1 {\n                  border-right: 1px solid rgba(0, 0, 0, .3);\n                }\n                @media (prefers-color-scheme: dark) {\n                  body { color: #fff; background: #000; }\n                  .next-error-h1 {\n                    border-right: 1px solid rgba(255, 255, 255, .3);\n                  }\n                }"
                     }
                   }),
                   statusCode
                     ? /*#__PURE__*/ _react.default.createElement(
                         "h1",
                         {
+                          className: "next-error-h1",
                           style: styles.h1
                         },
                         statusCode
@@ -3578,8 +3580,6 @@
       Error.origGetInitialProps = _getInitialProps;
       var styles = {
         error: {
-          color: "#000",
-          background: "#fff",
           fontFamily:
             '-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
           height: "100vh",
@@ -3598,7 +3598,6 @@
         },
         h1: {
           display: "inline-block",
-          borderRight: "1px solid rgba(0, 0, 0,.3)",
           margin: 0,
           marginRight: "20px",
           padding: "10px 23px 10px 0",
@@ -3694,8 +3693,15 @@
         value: true
       });
       exports.escapeStringRegexp = escapeStringRegexp;
+      // regexp is based on https://github.com/sindresorhus/escape-string-regexp
+      var reHasRegExp = /[|\\{}()[\]^$+*?.-]/;
+      var reReplaceRegExp = /[|\\{}()[\]^$+*?.-]/g;
       function escapeStringRegexp(str) {
-        return str.replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&");
+        // see also: https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/escapeRegExp.js#L23
+        if (reHasRegExp.test(str)) {
+          return str.replace(reReplaceRegExp, "\\$&");
+        }
+        return str;
       } //# sourceMappingURL=escape-regexp.js.map
 
       /***/
Diff for index.html
@@ -19,7 +19,7 @@
       defer=""
     ></script>
     <script
-      src="/_next/static/chunks/main-d49dd75f151b9b34.js"
+      src="/_next/static/chunks/main-82abc31d875ace09.js"
       defer=""
     ></script>
     <script
Diff for link.html
@@ -19,7 +19,7 @@
       defer=""
     ></script>
     <script
-      src="/_next/static/chunks/main-d49dd75f151b9b34.js"
+      src="/_next/static/chunks/main-82abc31d875ace09.js"
       defer=""
     ></script>
     <script
Diff for withRouter.html
@@ -19,7 +19,7 @@
       defer=""
     ></script>
     <script
-      src="/_next/static/chunks/main-d49dd75f151b9b34.js"
+      src="/_next/static/chunks/main-82abc31d875ace09.js"
       defer=""
     ></script>
     <script

Please sign in to comment.