Ensure timed out prefetches are cleaned up correctly (#28899)
This applies the fix from the awesome investigation done in https://github.com/vercel/next.js/issues/28797 by @jayphelps and adds a test to ensure this is working as expected. It seems that the `route-loader` has a race condition while prefetching and if a script is executed before we have created a current "future" entry to resolve the entry stays in a pending state causing routes to hang so this handles the condition by ensuring pending/errored entries do not stay around. ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` Fixes: https://github.com/vercel/next.js/issues/28797 Fixes: https://github.com/vercel/next.js/issues/27783
This commit is contained in:
parent
4f8d883acd
commit
b71df190e5
4 changed files with 63 additions and 18 deletions
|
@ -60,8 +60,13 @@ function withFuture<T>(
|
|||
})
|
||||
map.set(key, (entry = { resolve: resolver!, future: prom }))
|
||||
return generator
|
||||
? // eslint-disable-next-line no-sequences
|
||||
generator().then((value) => (resolver(value), value))
|
||||
? generator()
|
||||
// eslint-disable-next-line no-sequences
|
||||
.then((value) => (resolver(value), value))
|
||||
.catch((err) => {
|
||||
map.delete(key)
|
||||
throw err
|
||||
})
|
||||
: prom
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
export default () => <p>Hello world</p>
|
||||
export default () => <p id="another">Hello world</p>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
nextServer,
|
||||
runNextCommand,
|
||||
startApp,
|
||||
stopApp,
|
||||
findPort,
|
||||
killApp,
|
||||
nextBuild,
|
||||
nextStart,
|
||||
waitFor,
|
||||
} from 'next-test-utils'
|
||||
import http from 'http'
|
||||
import httpProxy from 'http-proxy'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { join } from 'path'
|
||||
import { readFile } from 'fs-extra'
|
||||
|
@ -14,24 +16,62 @@ import { readFile } from 'fs-extra'
|
|||
jest.setTimeout(1000 * 60 * 5)
|
||||
|
||||
const appDir = join(__dirname, '../')
|
||||
let appPort
|
||||
let server
|
||||
let app
|
||||
let appPort
|
||||
let stallJs
|
||||
let proxyServer
|
||||
|
||||
describe('Prefetching Links in viewport', () => {
|
||||
beforeAll(async () => {
|
||||
await runNextCommand(['build', appDir])
|
||||
await nextBuild(appDir)
|
||||
const port = await findPort()
|
||||
app = await nextStart(appDir, port)
|
||||
appPort = await findPort()
|
||||
|
||||
app = nextServer({
|
||||
dir: join(__dirname, '../'),
|
||||
dev: false,
|
||||
quiet: true,
|
||||
const proxy = httpProxy.createProxyServer({
|
||||
target: `http://localhost:${port}`,
|
||||
})
|
||||
|
||||
server = await startApp(app)
|
||||
appPort = server.address().port
|
||||
proxyServer = http.createServer(async (req, res) => {
|
||||
if (stallJs && req.url.includes('chunks/pages/another')) {
|
||||
console.log('stalling request for', req.url)
|
||||
await new Promise((resolve) => setTimeout(resolve, 5 * 1000))
|
||||
}
|
||||
proxy.web(req, res)
|
||||
})
|
||||
|
||||
proxy.on('error', (err) => {
|
||||
console.warn('Failed to proxy', err)
|
||||
})
|
||||
|
||||
await new Promise((resolve) => {
|
||||
proxyServer.listen(appPort, () => resolve())
|
||||
})
|
||||
})
|
||||
afterAll(async () => {
|
||||
await killApp(app)
|
||||
proxyServer.close()
|
||||
})
|
||||
|
||||
it('should handle timed out prefetch correctly', async () => {
|
||||
try {
|
||||
stallJs = true
|
||||
const browser = await webdriver(appPort, '/')
|
||||
|
||||
await browser.elementByCss('#scroll-to-another').click()
|
||||
// wait for preload to timeout
|
||||
await waitFor(6 * 1000)
|
||||
|
||||
await browser
|
||||
.elementByCss('#link-another')
|
||||
.click()
|
||||
.waitForElementByCss('#another')
|
||||
|
||||
expect(await browser.elementByCss('#another').text()).toBe('Hello world')
|
||||
} finally {
|
||||
stallJs = false
|
||||
}
|
||||
})
|
||||
afterAll(() => stopApp(server))
|
||||
|
||||
it('should prefetch with link in viewport onload', async () => {
|
||||
let browser
|
||||
|
@ -280,6 +320,7 @@ describe('Prefetching Links in viewport', () => {
|
|||
|
||||
expect(await browser.eval('window.hadUnhandledReject')).toBeFalsy()
|
||||
|
||||
await browser.waitForElementByCss('#invalid-link')
|
||||
await browser.elementByCss('#invalid-link').moveTo()
|
||||
expect(await browser.eval('window.hadUnhandledReject')).toBeFalsy()
|
||||
})
|
||||
|
|
|
@ -20,7 +20,6 @@ const appDir = join(__dirname, '../')
|
|||
function runTests() {
|
||||
it('should cancel slow page loads on re-navigation', async () => {
|
||||
const browser = await webdriver(appPort, '/')
|
||||
await waitFor(5000)
|
||||
|
||||
await browser.elementByCss('#link-1').click()
|
||||
await waitFor(3000)
|
||||
|
|
Loading…
Reference in a new issue