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) + }) +})