From 88ed5269b5e0c76667780f4823b669c392bc6713 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 18 Jun 2021 19:12:20 +0200 Subject: [PATCH] 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 --- package.json | 1 + packages/next/build/webpack-config.ts | 10 ++-- packages/next/client/dev/amp-dev.js | 7 ++- test/integration/amphtml/test/index.test.js | 11 +--- .../build-output/test/index.test.js | 2 +- .../worker-webpack5/lib/sharedCode.js | 17 ++++++ .../integration/worker-webpack5/lib/worker.js | 8 +++ .../worker-webpack5/next.config.js | 1 + .../worker-webpack5/pages/index.js | 38 ++++++++++++ .../worker-webpack5/test/index.test.js | 58 +++++++++++++++++++ yarn.lock | 5 ++ 11 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 test/integration/worker-webpack5/lib/sharedCode.js create mode 100644 test/integration/worker-webpack5/lib/worker.js create mode 100644 test/integration/worker-webpack5/next.config.js create mode 100644 test/integration/worker-webpack5/pages/index.js create mode 100644 test/integration/worker-webpack5/test/index.test.js diff --git a/package.json b/package.json index 81841b9b67333..0eaf0e8a542d9 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 9919302726da5..434e6a71e6c67 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -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 diff --git a/packages/next/client/dev/amp-dev.js b/packages/next/client/dev/amp-dev.js index c4c829a680f53..c10a5ac1736f2 100644 --- a/packages/next/client/dev/amp-dev.js +++ b/packages/next/client/dev/amp-dev.js @@ -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 diff --git a/test/integration/amphtml/test/index.test.js b/test/integration/amphtml/test/index.test.js index 02a8f3cafdd40..85d8d4385f0b0 100644 --- a/test/integration/amphtml/test/index.test.js +++ b/test/integration/amphtml/test/index.test.js @@ -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() } }) diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index 5893d7aa14569..233a760a02387 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -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 ) diff --git a/test/integration/worker-webpack5/lib/sharedCode.js b/test/integration/worker-webpack5/lib/sharedCode.js new file mode 100644 index 0000000000000..da7cd7ab6dd3d --- /dev/null +++ b/test/integration/worker-webpack5/lib/sharedCode.js @@ -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) + } +} diff --git a/test/integration/worker-webpack5/lib/worker.js b/test/integration/worker-webpack5/lib/worker.js new file mode 100644 index 0000000000000..d34bcedd68d23 --- /dev/null +++ b/test/integration/worker-webpack5/lib/worker.js @@ -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) diff --git a/test/integration/worker-webpack5/next.config.js b/test/integration/worker-webpack5/next.config.js new file mode 100644 index 0000000000000..4ba52ba2c8df6 --- /dev/null +++ b/test/integration/worker-webpack5/next.config.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/test/integration/worker-webpack5/pages/index.js b/test/integration/worker-webpack5/pages/index.js new file mode 100644 index 0000000000000..61a7c70a03c09 --- /dev/null +++ b/test/integration/worker-webpack5/pages/index.js @@ -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 ( +
+

$RefreshRegistry repro

+
Web: {expensiveWebStatus}
+
Worker: {expensiveWorkerStatus}
+
+ ) +} diff --git a/test/integration/worker-webpack5/test/index.test.js b/test/integration/worker-webpack5/test/index.test.js new file mode 100644 index 0000000000000..70d410cc6d602 --- /dev/null +++ b/test/integration/worker-webpack5/test/index.test.js @@ -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() + }) +}) diff --git a/yarn.lock b/yarn.lock index e416b69f99533..e82fc069824dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"