From 5a9a478cc84d013515d62d814f1f746cae5ffdff Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 17 Jan 2020 14:13:51 -0600 Subject: [PATCH] Fix hydration with custom _app and granular chunks (#10144) * Add failing hydration test * Add importing of next/router to _app * Fix type * Update _app check for windows * Remove babel fix * Update to use webpack to require next/router --- packages/next/build/entries.ts | 11 ++++++- packages/next/pages/_app.tsx | 1 - test/integration/hydration/pages/_app.js | 1 + test/integration/hydration/pages/_document.js | 16 +++++++++ test/integration/hydration/pages/details.js | 6 ++++ test/integration/hydration/pages/index.js | 6 ++++ test/integration/hydration/test/index.test.js | 33 +++++++++++++++++++ 7 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/integration/hydration/pages/_app.js create mode 100644 test/integration/hydration/pages/_document.js create mode 100644 test/integration/hydration/pages/details.js create mode 100644 test/integration/hydration/pages/index.js create mode 100644 test/integration/hydration/test/index.test.js diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index f51675d36655c16..a518b7b9877c69c 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -115,10 +115,19 @@ export function createEntrypoints( } if (!isApiRoute) { - client[bundlePath] = `next-client-pages-loader?${stringify({ + const pageLoader = `next-client-pages-loader?${stringify({ page, absolutePagePath, })}!` + + // Make sure next/router is a dependency of _app or else granularChunks + // might cause the router to not be able to load causing hydration + // to fail + + client[bundlePath] = + page === '/_app' + ? [pageLoader, require.resolve('../client/router')] + : pageLoader } }) diff --git a/packages/next/pages/_app.tsx b/packages/next/pages/_app.tsx index c4041f9613d76a5..3971441a24339ef 100644 --- a/packages/next/pages/_app.tsx +++ b/packages/next/pages/_app.tsx @@ -7,7 +7,6 @@ import { AppPropsType, } from '../next-server/lib/utils' import { Router } from '../client/router' -import '../client/router' export { AppInitialProps } diff --git a/test/integration/hydration/pages/_app.js b/test/integration/hydration/pages/_app.js new file mode 100644 index 000000000000000..e3c005467a1695a --- /dev/null +++ b/test/integration/hydration/pages/_app.js @@ -0,0 +1 @@ +export default ({ Component, pageProps }) => diff --git a/test/integration/hydration/pages/_document.js b/test/integration/hydration/pages/_document.js new file mode 100644 index 000000000000000..d4d66d494410fb1 --- /dev/null +++ b/test/integration/hydration/pages/_document.js @@ -0,0 +1,16 @@ +import Document, { Head, Html, Main, NextScript } from 'next/document' +import React from 'react' + +class WeddingDocument extends Document { + render() { + return ( + + +
+ + + ) + } +} + +export default WeddingDocument diff --git a/test/integration/hydration/pages/details.js b/test/integration/hydration/pages/details.js new file mode 100644 index 000000000000000..0acf307fdefc8c7 --- /dev/null +++ b/test/integration/hydration/pages/details.js @@ -0,0 +1,6 @@ +export default () => { + if (typeof window !== 'undefined') { + window.didHydrate = true + } + return 'details' +} diff --git a/test/integration/hydration/pages/index.js b/test/integration/hydration/pages/index.js new file mode 100644 index 000000000000000..b0989ad629fb61d --- /dev/null +++ b/test/integration/hydration/pages/index.js @@ -0,0 +1,6 @@ +export default () => { + if (typeof window !== 'undefined') { + window.didHydrate = true + } + return 'index' +} diff --git a/test/integration/hydration/test/index.test.js b/test/integration/hydration/test/index.test.js new file mode 100644 index 000000000000000..5a18abec58403b1 --- /dev/null +++ b/test/integration/hydration/test/index.test.js @@ -0,0 +1,33 @@ +/* eslint-env jest */ +/* global jasmine */ +import path from 'path' +import fs from 'fs-extra' +import webdriver from 'next-webdriver' +import { + nextBuild, + nextStart, + findPort, + waitFor, + killApp, +} from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1 +const appDir = path.join(__dirname, '..') +let app +let appPort + +describe('Hydration', () => { + beforeAll(async () => { + await fs.remove(path.join(appDir, '.next')) + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + + it('Hydrates correctly', async () => { + const browser = await webdriver(appPort, '/') + await waitFor(2000) + expect(await browser.eval('window.didHydrate')).toBe(true) + }) +})