diff --git a/packages/next/client/dev/dev-build-watcher.js b/packages/next/client/dev/dev-build-watcher.js index 682ddc1c91e920c..6b37a4f71a4f0c3 100644 --- a/packages/next/client/dev/dev-build-watcher.js +++ b/packages/next/client/dev/dev-build-watcher.js @@ -1,6 +1,6 @@ import { getEventSourceWrapper } from './error-overlay/eventsource' -export default function initializeBuildWatcher() { +export default function initializeBuildWatcher(toggleCallback) { const shadowHost = document.createElement('div') shadowHost.id = '__next-build-watcher' // Make sure container is fixed and on a high zIndex so it shows @@ -52,7 +52,8 @@ export default function initializeBuildWatcher() { }) function handleMessage(event) { - const obj = JSON.parse(event.data) + const obj = + typeof event === 'string' ? { action: event } : JSON.parse(event.data) // eslint-disable-next-line default-case switch (obj.action) { @@ -75,6 +76,8 @@ export default function initializeBuildWatcher() { } } + toggleCallback(handleMessage) + function updateContainer() { if (isBuilding) { container.classList.add(`${prefix}building`) diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index 3d7b64d7ba46806..1b6a1a3e307a218 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -33,6 +33,8 @@ initNext({ webpackHMR }) .then(({ renderCtx, render }) => { initOnDemandEntries({ assetPrefix: prefix }) + let buildIndicatorHandler = () => {} + function devPagesManifestListener(event) { if (event.data.indexOf('devPagesManifest') !== -1) { fetch(`${prefix}/_next/static/development/_devPagesManifest.json`) @@ -50,24 +52,34 @@ initNext({ webpackHMR }) if (pages.includes(router.pathname)) { console.log('Refreshing page data due to server-side change') - router.replace( - router.pathname + - '?' + - String( - querystring.assign( - querystring.urlQueryToSearchParams(router.query), - new URLSearchParams(location.search) - ) - ), - router.asPath - ) + buildIndicatorHandler('building') + + const clearIndicator = () => buildIndicatorHandler('built') + + router + .replace( + router.pathname + + '?' + + String( + querystring.assign( + querystring.urlQueryToSearchParams(router.query), + new URLSearchParams(location.search) + ) + ), + router.asPath + ) + .finally(clearIndicator) } } } devPagesManifestListener.unfiltered = true getEventSourceWrapper({}).addMessageListener(devPagesManifestListener) - if (process.env.__NEXT_BUILD_INDICATOR) initializeBuildWatcher() + if (process.env.__NEXT_BUILD_INDICATOR) { + initializeBuildWatcher((handler) => { + buildIndicatorHandler = handler + }) + } if ( process.env.__NEXT_PRERENDER_INDICATOR && // disable by default in electron diff --git a/test/integration/gssp-ssr-change-reloading/pages/gsp-blog/[post].js b/test/integration/gssp-ssr-change-reloading/pages/gsp-blog/[post].js index 187308da7565490..2077feda1dcef8e 100644 --- a/test/integration/gssp-ssr-change-reloading/pages/gsp-blog/[post].js +++ b/test/integration/gssp-ssr-change-reloading/pages/gsp-blog/[post].js @@ -13,9 +13,13 @@ export default function Gsp(props) { ) } -export const getStaticProps = ({ params }) => { +export const getStaticProps = async ({ params }) => { const count = 1 + if (params.post === 'second') { + await new Promise((resolve) => setTimeout(resolve, 2000)) + } + return { props: { count, diff --git a/test/integration/gssp-ssr-change-reloading/test/index.test.js b/test/integration/gssp-ssr-change-reloading/test/index.test.js index c6bfb6e58b4b9ff..1701a47de9e15b7 100644 --- a/test/integration/gssp-ssr-change-reloading/test/index.test.js +++ b/test/integration/gssp-ssr-change-reloading/test/index.test.js @@ -10,6 +10,19 @@ const appDir = join(__dirname, '..') let appPort let app +const installCheckVisible = (browser) => { + return browser.eval(`(function() { + window.checkInterval = setInterval(function() { + let watcherDiv = document.querySelector('#__next-build-watcher') + watcherDiv = watcherDiv.shadowRoot || watcherDiv + window.showedBuilder = window.showedBuilder || ( + watcherDiv.querySelector('div').className.indexOf('visible') > -1 + ) + if (window.showedBuilder) clearInterval(window.checkInterval) + }, 50) + })()`) +} + describe('GS(S)P Server-Side Change Reloading', () => { beforeAll(async () => { appPort = await findPort() @@ -62,6 +75,33 @@ describe('GS(S)P Server-Side Change Reloading', () => { ) }) + it('should show indicator when re-fetching data', async () => { + const browser = await webdriver(appPort, '/gsp-blog/second') + await installCheckVisible(browser) + await browser.eval(() => (window.beforeChange = 'hi')) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = new File(join(appDir, 'pages/gsp-blog/[post].js')) + page.replace('count = 1', 'count = 2') + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '2' + ) + expect(await browser.eval(() => window.beforeChange)).toBe('hi') + expect(await browser.eval(() => window.showedBuilder)).toBe(true) + page.restore() + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '1' + ) + }) + it('should update page when getStaticPaths is changed only', async () => { const browser = await webdriver(appPort, '/gsp-blog/first') await browser.eval(() => (window.beforeChange = 'hi'))