Add runtime to hotUpdateMainFilename (#26256)

Updates the hotUpdateChunk to include `[runtime]` for web workers support.

Fixes #26152
Fixes #19865
Fixes #26144

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added

## 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.

## Documentation / Examples

- [ ] Make sure the linting passes
This commit is contained in:
Tim Neutkens 2021-06-18 19:12:20 +02:00 committed by GitHub
parent 98acfaf8cc
commit 88ed5269b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 143 additions and 15 deletions

View file

@ -79,6 +79,7 @@
"eslint-plugin-react-hooks": "4.2.0",
"execa": "2.0.3",
"express": "4.17.0",
"faker": "5.5.3",
"faunadb": "2.6.1",
"firebase": "7.14.5",
"fs-extra": "9.0.0",

View file

@ -510,7 +510,7 @@ export default async function getBaseWebpackConfig(
// Contains various versions of the Webpack SplitChunksPlugin used in different build types
const splitChunksConfigs: {
[propName: string]: webpack.Options.SplitChunksOptions
[propName: string]: webpack.Options.SplitChunksOptions | false
} = {
dev: {
cacheGroups: {
@ -611,9 +611,9 @@ export default async function getBaseWebpackConfig(
}
// Select appropriate SplitChunksPlugin config for this build
let splitChunksConfig: webpack.Options.SplitChunksOptions
let splitChunksConfig: webpack.Options.SplitChunksOptions | false
if (dev) {
splitChunksConfig = splitChunksConfigs.dev
splitChunksConfig = isWebpack5 ? false : splitChunksConfigs.dev
} else {
splitChunksConfig = splitChunksConfigs.prodGranular
}
@ -940,7 +940,7 @@ export default async function getBaseWebpackConfig(
: {}),
// we must set publicPath to an empty value to override the default of
// auto which doesn't work in IE11
publicPath: '',
publicPath: `${config.assetPrefix || ''}/_next/`,
path:
isServer && isWebpack5 && !dev
? path.join(outputPath, 'chunks')
@ -959,7 +959,7 @@ export default async function getBaseWebpackConfig(
? 'static/webpack/[id].[fullhash].hot-update.js'
: 'static/webpack/[id].[hash].hot-update.js',
hotUpdateMainFilename: isWebpack5
? 'static/webpack/[fullhash].hot-update.json'
? 'static/webpack/[fullhash].[runtime].hot-update.json'
: 'static/webpack/[hash].hot-update.json',
// This saves chunks with the name given via `import()`
chunkFilename: isServer

View file

@ -37,7 +37,12 @@ async function tryApplyUpdates() {
return
}
try {
const res = await fetch(`${hotUpdatePath}${curHash}.hot-update.json`)
const res = await fetch(
typeof __webpack_runtime_id__ !== 'undefined'
? // eslint-disable-next-line no-undef
`${hotUpdatePath}${curHash}.${__webpack_runtime_id__}.hot-update.json`
: `${hotUpdatePath}${curHash}.hot-update.json`
)
const jsonData = await res.json()
const curPage = page === '/' ? 'index' : page
// webpack 5 uses an array instead

View file

@ -402,6 +402,8 @@ describe('AMP Usage', () => {
it('should not reload unless the page is edited for an AMP page', async () => {
let browser
const hmrTestPagePath = join(__dirname, '../', 'pages', 'hmr', 'test.js')
const originalContent = readFileSync(hmrTestPagePath, 'utf8')
try {
await renderViaHTTP(dynamicAppPort, '/hmr/test')
@ -409,15 +411,7 @@ describe('AMP Usage', () => {
await check(() => browser.elementByCss('p').text(), /I'm an AMP page!/)
const origDate = await browser.elementByCss('span').text()
const hmrTestPagePath = join(
__dirname,
'../',
'pages',
'hmr',
'test.js'
)
const originalContent = readFileSync(hmrTestPagePath, 'utf8')
const editedContent = originalContent.replace(
`This is the hot AMP page.`,
'replaced it!'
@ -456,6 +450,7 @@ describe('AMP Usage', () => {
await check(() => getBrowserBodyText(browser), /I'm an AMP page!/)
} finally {
writeFileSync(hmrTestPagePath, originalContent, 'utf8')
await browser.close()
}
})

View file

@ -144,7 +144,7 @@ describe('Build Output', () => {
const webpackSizeValue = webpackSize.endsWith('kB')
? parseFloat(webpackSize)
: parseFloat(webpackSize) / 1000
expect(webpackSizeValue).toBeCloseTo(gz ? 0.76 : 1.45, 2)
expect(webpackSizeValue).toBeCloseTo(gz ? 0.766 : 1.46, 2)
expect(webpackSize.endsWith('kB') || webpackSize.endsWith(' B')).toBe(
true
)

View file

@ -0,0 +1,17 @@
export function Expensive() {
const start = performance.now()
let i = 99999
const bigArray = []
while (--i) {
bigArray.push(i)
}
const endTime = performance.now()
if (typeof window === 'undefined') {
console.log('[WORKER] Completed expensive function in', endTime - start)
} else {
console.log('[WEB] Completed expensive function in', endTime - start)
}
}

View file

@ -0,0 +1,8 @@
import { Expensive } from './sharedCode'
import faker from 'faker'
// Ensure a large libraries is added so that splitChunks would trigger if enabled.
console.log(faker)
Expensive()
self.postMessage(true)

View file

@ -0,0 +1 @@
module.exports = {}

View file

@ -0,0 +1,38 @@
import * as React from 'react'
import { Expensive } from '../lib/sharedCode'
export default function Home() {
const [expensiveWebStatus, setExpensiveWebStatus] = React.useState('WAIT')
const [expensiveWorkerStatus, setExpensiveWorkerComplete] = React.useState(
'WAIT'
)
const worker = React.useRef()
React.useEffect(() => {
worker.current = new Worker(new URL('../lib/worker.js', import.meta.url))
worker.current.addEventListener('message', ({ data }) => {
if (data) {
setExpensiveWorkerComplete('PASS')
}
})
worker.current.addEventListener('error', (data) => {
setExpensiveWorkerComplete('FAIL')
})
}, [worker, setExpensiveWorkerComplete])
React.useEffect(() => {
try {
Expensive()
setExpensiveWebStatus('PASS')
} catch {
setExpensiveWebStatus('FAIL')
}
}, [])
return (
<main>
<h1>$RefreshRegistry repro</h1>
<div id="web-status">Web: {expensiveWebStatus}</div>
<div id="worker-status">Worker: {expensiveWorkerStatus}</div>
</main>
)
}

View file

@ -0,0 +1,58 @@
/* eslint-env jest */
import {
check,
findPort,
killApp,
launchApp,
nextStart,
nextBuild,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
import { join } from 'path'
const appDir = join(__dirname, '../')
jest.setTimeout(1000 * 60 * 2)
let appPort
let app
const runTests = () => {
it('should pass on both client and worker', async () => {
let browser
try {
browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#web-status')
await check(() => browser.elementByCss('#web-status').text(), /PASS/i)
await browser.waitForElementByCss('#worker-status')
await check(() => browser.elementByCss('#worker-status').text(), /PASS/i)
} finally {
if (browser) {
await browser.close()
}
}
})
}
describe('Web Workers with webpack 5', () => {
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
describe('server mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
})

View file

@ -8488,6 +8488,11 @@ extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
faker@5.5.3:
version "5.5.3"
resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e"
integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==
fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"