Skip to content

Commit

Permalink
fix: load webpack hook before config is required (vercel#22583)
Browse files Browse the repository at this point in the history
This pull request ensures the webpack hook is installed before an attempt is made to load the configuration.

This pull request is tested by the PnP tests, which should now be passing as a result of this change.

---

Fixes vercel#21679
  • Loading branch information
Timer authored Feb 27, 2021
1 parent 1ebc9bb commit 04f37d0
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 118 deletions.
11 changes: 1 addition & 10 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import semver from 'next/dist/compiled/semver'
// @ts-ignore No typings yet
import TerserPlugin from './webpack/plugins/terser-webpack-plugin/src/index.js'
import path from 'path'
import {
webpack,
isWebpack5,
init as initWebpack,
} from 'next/dist/compiled/webpack/webpack'
import { webpack, isWebpack5 } from 'next/dist/compiled/webpack/webpack'
import {
DOT_NEXT_ALIAS,
NEXT_PROJECT_ROOT,
Expand Down Expand Up @@ -208,11 +204,6 @@ export default async function getBaseWebpackConfig(
rewrites: Rewrite[]
}
): Promise<webpack.Configuration> {
initWebpack(!!config.future?.webpack5)
// hook the Node.js require so that webpack requires are
// routed to the bundled and now initialized webpack version
require('./webpack/require-hook')

let plugins: PluginMetaData[] = []
let babelPresetPlugins: { dir: string; config: any }[] = []

Expand Down
2 changes: 1 addition & 1 deletion packages/next/bundles/webpack/packages/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ exports.init = function (useWebpack5) {

exports.onWebpackInit = function (cb) {
if (initializedWebpack5 || initializedWebpack4) cb()
initFns.push(cb)
else initFns.push(cb)
}
2 changes: 1 addition & 1 deletion packages/next/compiled/webpack/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ exports.init = function (useWebpack5) {

exports.onWebpackInit = function (cb) {
if (initializedWebpack5 || initializedWebpack4) cb()
initFns.push(cb)
else initFns.push(cb)
}
104 changes: 104 additions & 0 deletions packages/next/next-server/server/config-shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import os from 'os'
import { Header, Redirect, Rewrite } from '../../lib/load-custom-routes'
import { imageConfigDefault } from './image-config'

export type DomainLocales = Array<{
http?: true
domain: string
locales?: string[]
defaultLocale: string
}>

export type NextConfig = { [key: string]: any } & {
i18n?: {
locales: string[]
defaultLocale: string
domains?: DomainLocales
localeDetection?: false
} | null

headers?: () => Promise<Header[]>
rewrites?: () => Promise<Rewrite[]>
redirects?: () => Promise<Redirect[]>

trailingSlash?: boolean

future: {
strictPostcssConfiguration: boolean
excludeDefaultMomentLocales: boolean
webpack5: boolean
}
}

export const defaultConfig: NextConfig = {
env: [],
webpack: null,
webpackDevMiddleware: null,
distDir: '.next',
assetPrefix: '',
configOrigin: 'default',
useFileSystemPublicRoutes: true,
generateBuildId: () => null,
generateEtags: true,
pageExtensions: ['tsx', 'ts', 'jsx', 'js'],
target: 'server',
poweredByHeader: true,
compress: true,
analyticsId: process.env.VERCEL_ANALYTICS_ID || '',
images: imageConfigDefault,
devIndicators: {
buildActivity: true,
},
onDemandEntries: {
maxInactiveAge: 60 * 1000,
pagesBufferLength: 2,
},
amp: {
canonicalBase: '',
},
basePath: '',
sassOptions: {},
trailingSlash: false,
i18n: null,
productionBrowserSourceMaps: false,
experimental: {
cpus: Math.max(
1,
(Number(process.env.CIRCLE_NODE_TOTAL) ||
(os.cpus() || { length: 1 }).length) - 1
),
plugins: false,
profiling: false,
sprFlushToDisk: true,
reactMode: 'legacy',
workerThreads: false,
pageEnv: false,
optimizeFonts: false,
optimizeImages: false,
optimizeCss: false,
scrollRestoration: false,
scriptLoader: false,
stats: false,
},
future: {
strictPostcssConfiguration: false,
excludeDefaultMomentLocales: false,
webpack5: Number(process.env.NEXT_PRIVATE_TEST_WEBPACK5_MODE) > 0,
},
serverRuntimeConfig: {},
publicRuntimeConfig: {},
reactStrictMode: false,
}

export function normalizeConfig(phase: string, config: any) {
if (typeof config === 'function') {
config = config(phase, { defaultConfig })

if (typeof config.then === 'function') {
throw new Error(
'> Promise returned in next config. https://err.sh/vercel/next.js/promise-in-next-config'
)
}
}
return config
}
61 changes: 61 additions & 0 deletions packages/next/next-server/server/config-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Worker from 'jest-worker'
import findUp from 'next/dist/compiled/find-up'
import { init as initWebpack } from 'next/dist/compiled/webpack/webpack'
import { CONFIG_FILE } from '../lib/constants'
import { NextConfig, normalizeConfig } from './config-shared'

let installed: boolean = false

export function install(useWebpack5: boolean) {
if (installed) {
return
}
installed = true

initWebpack(useWebpack5)

// hook the Node.js require so that webpack requires are
// routed to the bundled and now initialized webpack version
require('../../build/webpack/require-hook')
}

export async function shouldLoadWithWebpack5(
phase: string,
dir: string
): Promise<boolean> {
const path = await findUp(CONFIG_FILE, {
cwd: dir,
})

// No `next.config.js`:
if (!path?.length) {
return false // TODO: return true to default to webpack 5
}

// Default to webpack 4 for backwards compatibility on boot:
install(false)

const userConfigModule = require(path)
const userConfig: Partial<NextConfig> = normalizeConfig(
phase,
userConfigModule.default || userConfigModule
)

// TODO: enable commented branch to enable webpack 5
return userConfig.future?.webpack5 === true /* || !userConfig.webpack */
}

export async function loadWebpackHook(phase: string, dir: string) {
let useWebpack5 = false
const worker: any = new Worker(__filename, { enableWorkerThreads: true })
try {
useWebpack5 = Boolean(await worker.shouldLoadWithWebpack5(phase, dir))
} catch {
// If this errors, it likely will do so again upon boot, so we just swallow
// it here.
} finally {
worker.end()
}

install(useWebpack5)
}
111 changes: 7 additions & 104 deletions packages/next/next-server/server/config.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,18 @@
import chalk from 'chalk'
import findUp from 'next/dist/compiled/find-up'
import os from 'os'
import { basename, extname } from 'path'
import { execOnce } from '../lib/utils'
import * as Log from '../../build/output/log'
import { CONFIG_FILE } from '../lib/constants'
import { Header, Rewrite, Redirect } from '../../lib/load-custom-routes'
import { execOnce } from '../lib/utils'
import { defaultConfig, normalizeConfig } from './config-shared'
import { loadWebpackHook } from './config-utils'
import { ImageConfig, imageConfigDefault, VALID_LOADERS } from './image-config'

export { DomainLocales, NextConfig, normalizeConfig } from './config-shared'

const targets = ['server', 'serverless', 'experimental-serverless-trace']
const reactModes = ['legacy', 'blocking', 'concurrent']

export type DomainLocales = Array<{
http?: true
domain: string
locales?: string[]
defaultLocale: string
}>

export type NextConfig = { [key: string]: any } & {
i18n?: {
locales: string[]
defaultLocale: string
domains?: DomainLocales
localeDetection?: false
} | null

headers?: () => Promise<Header[]>
rewrites?: () => Promise<Rewrite[]>
redirects?: () => Promise<Redirect[]>

trailingSlash?: boolean

future: {
strictPostcssConfiguration: boolean
excludeDefaultMomentLocales: boolean
webpack5: boolean
}
}

const defaultConfig: NextConfig = {
env: [],
webpack: null,
webpackDevMiddleware: null,
distDir: '.next',
assetPrefix: '',
configOrigin: 'default',
useFileSystemPublicRoutes: true,
generateBuildId: () => null,
generateEtags: true,
pageExtensions: ['tsx', 'ts', 'jsx', 'js'],
target: 'server',
poweredByHeader: true,
compress: true,
analyticsId: process.env.VERCEL_ANALYTICS_ID || '',
images: imageConfigDefault,
devIndicators: {
buildActivity: true,
},
onDemandEntries: {
maxInactiveAge: 60 * 1000,
pagesBufferLength: 2,
},
amp: {
canonicalBase: '',
},
basePath: '',
sassOptions: {},
trailingSlash: false,
i18n: null,
productionBrowserSourceMaps: false,
experimental: {
cpus: Math.max(
1,
(Number(process.env.CIRCLE_NODE_TOTAL) ||
(os.cpus() || { length: 1 }).length) - 1
),
plugins: false,
profiling: false,
sprFlushToDisk: true,
reactMode: 'legacy',
workerThreads: false,
pageEnv: false,
optimizeFonts: false,
optimizeImages: false,
optimizeCss: false,
scrollRestoration: false,
scriptLoader: false,
stats: false,
},
future: {
strictPostcssConfiguration: false,
excludeDefaultMomentLocales: false,
webpack5: Number(process.env.NEXT_PRIVATE_TEST_WEBPACK5_MODE) > 0,
},
serverRuntimeConfig: {},
publicRuntimeConfig: {},
reactStrictMode: false,
}

const experimentalWarning = execOnce(() => {
Log.warn(chalk.bold('You have enabled experimental feature(s).'))
Log.warn(
Expand Down Expand Up @@ -474,24 +388,13 @@ function assignDefaults(userConfig: { [key: string]: any }) {
return result
}

export function normalizeConfig(phase: string, config: any) {
if (typeof config === 'function') {
config = config(phase, { defaultConfig })

if (typeof config.then === 'function') {
throw new Error(
'> Promise returned in next config. https://err.sh/vercel/next.js/promise-in-next-config'
)
}
}
return config
}

export default async function loadConfig(
phase: string,
dir: string,
customConfig?: object | null
) {
await loadWebpackHook(phase, dir)

if (customConfig) {
return assignDefaults({ configOrigin: 'server', ...customConfig })
}
Expand Down
9 changes: 7 additions & 2 deletions test-pnp.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare -a testCases=(
# Tests the webpack require hook
"progressive-web-app"
"progressive-web-app"

"with-typescript"
"with-next-sass"
# Tests @next/mdx
Expand All @@ -11,6 +11,7 @@ declare -a testCases=(
)

set -e
set -x

# Speeds up testing locally
export CI=1
Expand All @@ -30,6 +31,10 @@ do

touch yarn.lock
yarn set version berry

# Temporary fix for https://github.com/yarnpkg/berry/issues/2514:
yarn set version from sources

yarn config set pnpFallbackMode none
yarn config set enableGlobalCache true
yarn link --all --private -r ../..
Expand Down

0 comments on commit 04f37d0

Please sign in to comment.