Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure error message prints next.config.mjs #30152

Merged
merged 6 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs/api-reference/next.config.js/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: learn more about the configuration file used by Next.js to handle y

# next.config.js

For custom advanced behavior of Next.js, you can create a `next.config.js` in the root of your project directory (next to `package.json`).
For custom advanced configuration of Next.js, you can create a `next.config.js` or `next.config.mjs` file in the root of your project directory (next to `package.json`).

`next.config.js` is a regular Node.js module, not a JSON file. It gets used by the Next.js server and build phases, and it's not included in the browser build.

Expand All @@ -21,6 +21,19 @@ const nextConfig = {
module.exports = nextConfig
```

If you need [ECMAScript modules](https://nodejs.org/api/esm.html), you can use `next.config.mjs`:

```js
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
/* config options here */
}

export default nextConfig
```

You can also use a function:

```js
Expand Down
8 changes: 5 additions & 3 deletions errors/next-config-error.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

#### Why This Error Occurred

When attempting to load your `next.config.js` file an error occurred. This could be due to a syntax error or attempting to `require` a module that wasn't available.
When attempting to load your `next.config.js` or `next.config.mjs` file, an error occurred. This could be due to a syntax error or attempting to `require`/`import` a module that wasn't available.

#### Possible Ways to Fix It

See the error message in your terminal where you started `next` to see more context. The `next.config.js` file is not transpiled by Next.js currently so ensure only features supported by your current node.js version are being used.
See the error message in your terminal where you started `next` to see more context.

> Note: This config file is not transpiled by Next.js, so only use features supported by your current Node.js version.

### Useful Links

- [next.config.js documentation](https://nextjs.org/docs/api-reference/next.config.js/introduction)
- [node.js version feature chart](https://node.green/)
- [Node.js version feature chart](https://node.green/)
3 changes: 3 additions & 0 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,7 @@ export default async function build(
} = await staticCheckSpan.traceAsyncFn(async () => {
process.env.NEXT_PHASE = PHASE_PRODUCTION_BUILD

const configFileName = config.configFileName
const runtimeEnvConfig = {
publicRuntimeConfig: config.publicRuntimeConfig,
serverRuntimeConfig: config.serverRuntimeConfig,
Expand Down Expand Up @@ -787,6 +788,7 @@ export default async function build(
'/_error',
distDir,
isLikeServerless,
configFileName,
runtimeEnvConfig,
config.httpAgentOptions,
config.i18n?.locales,
Expand Down Expand Up @@ -853,6 +855,7 @@ export default async function build(
page,
distDir,
isLikeServerless,
configFileName,
runtimeEnvConfig,
config.httpAgentOptions,
config.i18n?.locales,
Expand Down
5 changes: 4 additions & 1 deletion packages/next/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ export async function getJsPageSizeInKb(
export async function buildStaticPaths(
page: string,
getStaticPaths: GetStaticPaths,
configFileName: string,
locales?: string[],
defaultLocale?: string
): Promise<
Expand Down Expand Up @@ -788,7 +789,7 @@ export async function buildStaticPaths(

if (entry.locale && !locales?.includes(entry.locale)) {
throw new Error(
`Invalid locale returned from getStaticPaths for ${page}, the locale ${entry.locale} is not specified in next.config.js`
`Invalid locale returned from getStaticPaths for ${page}, the locale ${entry.locale} is not specified in ${configFileName}`
)
}
const curLocale = entry.locale || defaultLocale || ''
Expand Down Expand Up @@ -817,6 +818,7 @@ export async function isPageStatic(
page: string,
distDir: string,
serverless: boolean,
configFileName: string,
runtimeEnvConfig: any,
httpAgentOptions: NextConfigComplete['httpAgentOptions'],
locales?: string[],
Expand Down Expand Up @@ -925,6 +927,7 @@ export async function isPageStatic(
} = await buildStaticPaths(
page,
mod.getStaticPaths!,
configFileName,
locales,
defaultLocale
))
Expand Down
6 changes: 3 additions & 3 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1126,7 +1126,7 @@ export default async function getBaseWebpackConfig(
...Object.keys(config.env).reduce((acc, key) => {
if (/^(?:NODE_.+)|^(?:__.+)$/i.test(key)) {
throw new Error(
`The key "${key}" under "env" in next.config.js is not allowed. https://nextjs.org/docs/messages/env-key-not-allowed`
`The key "${key}" under "env" in ${config.configFileName} is not allowed. https://nextjs.org/docs/messages/env-key-not-allowed`
)
}

Expand Down Expand Up @@ -1527,7 +1527,7 @@ export default async function getBaseWebpackConfig(

if (!webpackConfig) {
throw new Error(
'Webpack config is undefined. You may have forgot to return properly from within the "webpack" method of your next.config.js.\n' +
`Webpack config is undefined. You may have forgot to return properly from within the "webpack" method of your ${config.configFileName}.\n` +
'See more info here https://nextjs.org/docs/messages/undefined-webpack-config'
)
}
Expand Down Expand Up @@ -1731,7 +1731,7 @@ export default async function getBaseWebpackConfig(

if (foundTsRule) {
console.warn(
'\n@zeit/next-typescript is no longer needed since Next.js has built-in support for TypeScript now. Please remove it from your next.config.js and your .babelrc\n'
`\n@zeit/next-typescript is no longer needed since Next.js has built-in support for TypeScript now. Please remove it from your ${config.configFileName} and your .babelrc\n`
)
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/next/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type NextConfigComplete = Required<NextConfig> & {
typescript: Required<TypeScriptConfig>
configOrigin?: string
configFile?: string
configFileName: string
}

export interface I18NConfig {
Expand Down
18 changes: 10 additions & 8 deletions packages/next/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ const experimentalWarning = execOnce(() => {
})

function assignDefaults(userConfig: { [key: string]: any }) {
const configFileName = userConfig.configFileName || 'next.config.js'
if (typeof userConfig.exportTrailingSlash !== 'undefined') {
console.warn(
chalk.yellow.bold('Warning: ') +
'The "exportTrailingSlash" option has been renamed to "trailingSlash". Please update your next.config.js.'
`The "exportTrailingSlash" option has been renamed to "trailingSlash". Please update your ${configFileName}.`
)
if (typeof userConfig.trailingSlash === 'undefined') {
userConfig.trailingSlash = userConfig.exportTrailingSlash
Expand All @@ -45,7 +46,7 @@ function assignDefaults(userConfig: { [key: string]: any }) {
if (typeof userConfig.experimental?.reactMode !== 'undefined') {
console.warn(
chalk.yellow.bold('Warning: ') +
'The experimental "reactMode" option has been replaced with "reactRoot". Please update your next.config.js.'
`The experimental "reactMode" option has been replaced with "reactRoot". Please update your ${configFileName}.`
)
if (typeof userConfig.experimental?.reactRoot === 'undefined') {
userConfig.experimental.reactRoot = ['concurrent', 'blocking'].includes(
Expand Down Expand Up @@ -353,14 +354,14 @@ function assignDefaults(userConfig: { [key: string]: any }) {

if (result.webpack5 === false) {
throw new Error(
'Webpack 4 is no longer supported in Next.js. Please upgrade to webpack 5 by removing "webpack5: false" from next.config.js. https://nextjs.org/docs/messages/webpack5'
`Webpack 4 is no longer supported in Next.js. Please upgrade to webpack 5 by removing "webpack5: false" from ${configFileName}. https://nextjs.org/docs/messages/webpack5`
)
}

if (result.experimental && 'nftTracing' in (result.experimental as any)) {
// TODO: remove this warning and assignment when we leave experimental phase
Log.warn(
`Experimental \`nftTracing\` has been renamed to \`outputFileTracing\`. Please update your next.config.js file accordingly.`
`Experimental \`nftTracing\` has been renamed to \`outputFileTracing\`. Please update your ${configFileName} file accordingly.`
)
result.experimental.outputFileTracing = (
result.experimental as any
Expand Down Expand Up @@ -538,6 +539,7 @@ export default async function loadConfig(

// If config file was found
if (path?.length) {
const configName = basename(path)
let userConfigModule: any

try {
Expand All @@ -546,9 +548,8 @@ export default async function loadConfig(
// with the `file://` protocol
userConfigModule = await import(pathToFileURL(path).href)
} catch (err) {
console.error(
chalk.red('Error:') +
' failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error'
Log.error(
`Failed to load ${configName}, see more info here https://nextjs.org/docs/messages/next-config-error`
)
throw err
}
Expand All @@ -559,7 +560,7 @@ export default async function loadConfig(

if (Object.keys(userConfig).length === 0) {
Log.warn(
'Detected next.config.js, no exported configuration found. https://nextjs.org/docs/messages/empty-configuration'
`Detected ${configName}, no exported configuration found. https://nextjs.org/docs/messages/empty-configuration`
)
}

Expand Down Expand Up @@ -587,6 +588,7 @@ export default async function loadConfig(
return assignDefaults({
configOrigin: relative(dir, path),
configFile: path,
configFileName: configName,
...userConfig,
}) as NextConfigComplete
} else {
Expand Down
34 changes: 28 additions & 6 deletions test/integration/config-syntax-error/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { join } from 'path'
import { nextBuild } from 'next-test-utils'

const appDir = join(__dirname, '..')
const nextConfig = join(appDir, 'next.config.js')
const nextConfigJS = join(appDir, 'next.config.js')
const nextConfigMJS = join(appDir, 'next.config.mjs')

describe('Invalid resolve alias', () => {
it('should show relevant error when webpack resolve alias is wrong', async () => {
describe('Invalid config syntax', () => {
it('should error when next.config.js contains syntax error', async () => {
await fs.writeFile(
nextConfig,
nextConfigJS,
`
module.exports = {
reactStrictMode: true,,
Expand All @@ -19,10 +20,31 @@ describe('Invalid resolve alias', () => {
const { stderr } = await nextBuild(appDir, undefined, {
stderr: true,
})
await fs.remove(nextConfig)
await fs.remove(nextConfigJS)

expect(stderr).toContain(
'Error: failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error'
'error - Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error'
)
expect(stderr).toContain('SyntaxError')
})

it('should error when next.config.mjs contains syntax error', async () => {
await fs.writeFile(
nextConfigMJS,
`
const config = {
reactStrictMode: true,,
}
export default config
`
)
const { stderr } = await nextBuild(appDir, undefined, {
stderr: true,
})
await fs.remove(nextConfigMJS)

expect(stderr).toContain(
'error - Failed to load next.config.mjs, see more info here https://nextjs.org/docs/messages/next-config-error'
)
expect(stderr).toContain('SyntaxError')
})
Expand Down