Skip to content

Commit

Permalink
feat: automatically enable webpack 5 (vercel#22323)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Neutkens <[email protected]>
Co-authored-by: Tim Neutkens <[email protected]>
  • Loading branch information
3 people authored Mar 24, 2021
1 parent 5c7475a commit 437c35f
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 34 deletions.
7 changes: 5 additions & 2 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,10 @@ export default async function getBaseWebpackConfig(
}

const clientResolveRewrites = require.resolve(
'next/dist/next-server/lib/router/utils/resolve-rewrites'
'../next-server/lib/router/utils/resolve-rewrites'
)
const clientResolveRewritesNoop = require.resolve(
'next/dist/next-server/lib/router/utils/resolve-rewrites-noop'
'../next-server/lib/router/utils/resolve-rewrites-noop'
)

const resolveConfig = {
Expand Down Expand Up @@ -386,6 +386,9 @@ export default async function getBaseWebpackConfig(
...getReactProfilingInProduction(),
[clientResolveRewrites]: hasRewrites
? clientResolveRewrites
: // With webpack 5 an alias can be pointed to false to noop
isWebpack5
? false
: clientResolveRewritesNoop,
},
...(isWebpack5 && !isServer
Expand Down
73 changes: 67 additions & 6 deletions packages/next/next-server/server/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,42 @@ export function install(useWebpack5: boolean) {
require('../../build/webpack/require-hook')
}

type CheckReasons =
| 'test-mode'
| 'no-config'
| 'future-flag'
| 'no-webpack-config'
| 'webpack-config'

type CheckResult = {
enabled: boolean
reason: CheckReasons
}

export async function shouldLoadWithWebpack5(
phase: string,
dir: string
): Promise<boolean> {
): Promise<CheckResult> {
await loadEnvConfig(dir, phase === PHASE_DEVELOPMENT_SERVER, Log)

const path = await findUp(CONFIG_FILE, {
cwd: dir,
})

if (Number(process.env.NEXT_PRIVATE_TEST_WEBPACK5_MODE) > 0) {
return true
return {
enabled: true,
reason: 'test-mode',
}
}

// No `next.config.js`:
if (!path?.length) {
return false // TODO: return true to default to webpack 5
// Use webpack 5 by default in new apps:
return {
enabled: true,
reason: 'no-config',
}
}

// Default to webpack 4 for backwards compatibility on boot:
Expand All @@ -49,15 +68,57 @@ export async function shouldLoadWithWebpack5(
userConfigModule.default || userConfigModule
)

// TODO: enable commented branch to enable webpack 5
return userConfig.future?.webpack5 === true /* || !userConfig.webpack */
// Opted-in manually
if (userConfig.future?.webpack5 === true) {
return {
enabled: true,
reason: 'future-flag',
}
}

// The user isn't configuring webpack
if (!userConfig.webpack) {
return {
enabled: true,
reason: 'no-webpack-config',
}
}

return {
enabled: false,
reason: 'webpack-config',
}
}

function reasonMessage(reason: CheckReasons) {
switch (reason) {
case 'future-flag':
return 'future.webpack5 option enabled'
case 'no-config':
return 'no next.config.js'
case 'no-webpack-config':
return 'no custom webpack configuration in next.config.js'
case 'test-mode':
return 'internal test mode'
default:
return ''
}
}

export async function loadWebpackHook(phase: string, dir: string) {
let useWebpack5 = false
const worker: any = new Worker(__filename, { enableWorkerThreads: false })
try {
useWebpack5 = Boolean(await worker.shouldLoadWithWebpack5(phase, dir))
const result: CheckResult = await worker.shouldLoadWithWebpack5(phase, dir)
if (result.enabled) {
Log.info(`Using webpack 5. Reason: ${reasonMessage(result.reason)}`)
} else {
Log.info(
'Using webpack 4. Reason: the next.config.js has custom webpack configuration',
reasonMessage(result.reason)
)
}
useWebpack5 = Boolean(result.enabled)
} catch {
// If this errors, it likely will do so again upon boot, so we just swallow
// it here.
Expand Down
1 change: 1 addition & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"crypto-browserify": "3.12.0",
"cssnano-simple": "1.2.2",
"domain-browser": "4.19.0",
"encoding": "0.1.13",
"eslint": "7.9.0",
"etag": "1.8.1",
"find-cache-dir": "3.3.1",
Expand Down
14 changes: 7 additions & 7 deletions test/integration/build-output/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,21 @@ describe('Build Output', () => {
expect(parseFloat(size)).toBeGreaterThan(0)
}

// should be no bigger than 265 bytes
expect(parseFloat(indexSize) - 266).toBeLessThanOrEqual(0)
// should be no bigger than 291 bytes
expect(parseFloat(indexSize) - 291).toBeLessThanOrEqual(0)
expect(indexSize.endsWith('B')).toBe(true)

// should be no bigger than 64.6 kb
expect(parseFloat(indexFirstLoad)).toBeCloseTo(64.6, 1)
// should be no bigger than 64.8 kb
expect(parseFloat(indexFirstLoad)).toBeCloseTo(65.4, 1)
expect(indexFirstLoad.endsWith('kB')).toBe(true)

expect(parseFloat(err404Size) - 3.7).toBeLessThanOrEqual(0)
expect(err404Size.endsWith('kB')).toBe(true)

expect(parseFloat(err404FirstLoad)).toBeCloseTo(67.8, 0)
expect(parseFloat(err404FirstLoad)).toBeCloseTo(68.5, 0)
expect(err404FirstLoad.endsWith('kB')).toBe(true)

expect(parseFloat(sharedByAll)).toBeCloseTo(64.4, 1)
expect(parseFloat(sharedByAll)).toBeCloseTo(65.1, 1)
expect(sharedByAll.endsWith('kB')).toBe(true)

if (_appSize.endsWith('kB')) {
Expand All @@ -115,7 +115,7 @@ describe('Build Output', () => {
expect(_appSize.endsWith(' B')).toBe(true)
}

expect(parseFloat(webpackSize) - 753).toBeLessThanOrEqual(0)
expect(parseFloat(webpackSize) - 950).toBeLessThanOrEqual(0)
expect(webpackSize.endsWith(' B')).toBe(true)

expect(parseFloat(mainSize) - 7.3).toBeLessThanOrEqual(0)
Expand Down
12 changes: 12 additions & 0 deletions test/integration/client-shallow-routing/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
killApp,
nextBuild,
nextStart,
check,
waitFor,
} from 'next-test-utils'

jest.setTimeout(1000 * 60 * 2)
Expand All @@ -24,22 +26,28 @@ const runTests = () => {
expect(props.params).toEqual({ slug: 'first' })

await browser.elementByCss('#add-query-shallow').click()
await waitFor(1000)

const props2 = JSON.parse(await browser.elementByCss('#props').text())
expect(props2).toEqual(props)

await browser.elementByCss('#remove-query-shallow').click()
await waitFor(1000)

const props3 = JSON.parse(await browser.elementByCss('#props').text())
expect(props3).toEqual(props)

await browser.elementByCss('#to-another').click()
await waitFor(1000)

await check(() => browser.elementByCss('#props').text(), /another/)

const props4 = JSON.parse(await browser.elementByCss('#props').text())
expect(props4.params).toEqual({ slug: 'another' })
expect(props4.random).not.toBe(props.random)

await browser.back()
await waitFor(1000)

const props5 = JSON.parse(await browser.elementByCss('#props').text())
expect(props5.params).toEqual({ slug: 'first' })
Expand All @@ -53,23 +61,27 @@ const runTests = () => {
expect(props.params).toEqual({ slug: 'first' })

await browser.elementByCss('#add-query-shallow').click()
await waitFor(1000)

const props2 = JSON.parse(await browser.elementByCss('#props').text())
expect(props2).toEqual(props)

await browser.elementByCss('#to-another').click()
await waitFor(1000)

const props3 = JSON.parse(await browser.elementByCss('#props').text())
expect(props3.params).toEqual({ slug: 'another' })
expect(props3.random).not.toBe(props2.random)

await browser.back()
await waitFor(1000)

const props4 = JSON.parse(await browser.elementByCss('#props').text())
expect(props4.params).toEqual({ slug: 'first' })
expect(props4.random).not.toBe(props3.random)

await browser.forward()
await waitFor(1000)

const props5 = JSON.parse(await browser.elementByCss('#props').text())
expect(props5.params).toEqual({ slug: 'another' })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('externalize next/dist/next-server', () => {
escapeStringRegexp(
`module.exports = require("next/dist/next-server/lib/head.js");`
) +
'$',
';?$',
'm'
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import webdriver from 'next-webdriver'
import { join } from 'path'

jest.setTimeout(1000 * 30)
jest.setTimeout(1000 * 60)

const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import webdriver from 'next-webdriver'
import { join } from 'path'

jest.setTimeout(1000 * 30)
jest.setTimeout(1000 * 60)

const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('TypeScript Image Component', () => {
it('should render the valid Image usage and not print error', async () => {
const html = await renderViaHTTP(appPort, '/valid', {})
expect(html).toMatch(/This is valid usage of the Image component/)
expect(output).not.toMatch(/Error/)
expect(output).not.toMatch(/Error: Image/)
})

it('should print error when invalid Image usage', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/integration/image-optimization/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from 'next-test-utils'
import fs from 'fs-extra'

jest.setTimeout(1000 * 30)
jest.setTimeout(1000 * 60)

const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const $comment = ({ gipQuery }) => {

return (
<>
<p>
<p id={query.comment}>
I am {query.comment} on {query.name}
</p>
<span>gip {gipQuery && gipQuery.name}</span>
Expand Down
2 changes: 1 addition & 1 deletion test/integration/src-dir-support/src/pages/[name]/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'
const Page = () => {
const router = useRouter()
const { query } = router
return <p>This is {query.name}</p>
return <p id={query.name}>This is {query.name}</p>
}

Page.getInitialProps = () => ({})
Expand Down
8 changes: 4 additions & 4 deletions test/integration/src-dir-support/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ function runTests() {
try {
browser = await webdriver(appPort, '/')
await browser.elementByCss('#view-post-1').click()
await browser.waitForElementByCss('p')
await browser.waitForElementByCss('p#post-1')

const text = await browser.elementByCss('p').text()
const text = await browser.elementByCss('p#post-1').text()
expect(text).toMatch(/this is.*?post-1/i)
} finally {
if (browser) await browser.close()
Expand All @@ -63,9 +63,9 @@ function runTests() {
try {
browser = await webdriver(appPort, '/')
await browser.elementByCss('#view-post-1-comment-1').click()
await browser.waitForElementByCss('p')
await browser.waitForElementByCss('p#comment-1')

const text = await browser.elementByCss('p').text()
const text = await browser.elementByCss('p#comment-1').text()
expect(text).toMatch(/i am.*comment-1.*on.*post-1/i)
} finally {
if (browser) await browser.close()
Expand Down
10 changes: 10 additions & 0 deletions test/integration/telemetry/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,11 @@ describe('Telemetry CLI', () => {
expect(event1).toMatch(/hasReportWebVitals.*?false/)

// Case 2: When _app.js exist with reportWebVitals function.
await fs.utimes(
path.join(appDir, 'pages', '_app_withreportwebvitals.empty'),
new Date(),
new Date()
)
await fs.rename(
path.join(appDir, 'pages', '_app_withreportwebvitals.empty'),
path.join(appDir, 'pages', '_app.js')
Expand All @@ -402,6 +407,11 @@ describe('Telemetry CLI', () => {
expect(event1).toMatch(/hasReportWebVitals.*?true/)

// Case 3: When _app.js exist without reportWebVitals function.
await fs.utimes(
path.join(appDir, 'pages', '_app_withoutreportwebvitals.empty'),
new Date(),
new Date()
)
await fs.rename(
path.join(appDir, 'pages', '_app_withoutreportwebvitals.empty'),
path.join(appDir, 'pages', '_app.js')
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5324,7 +5324,7 @@ [email protected]:
write-file-atomic "^2.0.0"
xdg-basedir "^3.0.0"

console-browserify@1.2.0, console-browserify@^1.1.0:
console-browserify@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
Expand Down Expand Up @@ -6493,19 +6493,19 @@ encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"

encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
dependencies:
iconv-lite "~0.4.13"

encoding@^0.1.12:
[email protected], encoding@^0.1.12:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
dependencies:
iconv-lite "^0.6.2"

encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
dependencies:
iconv-lite "~0.4.13"

end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
Expand Down

0 comments on commit 437c35f

Please sign in to comment.