Skip to content

Commit

Permalink
add experimental flag to allow forcing NODE_ENV=development in builds (
Browse files Browse the repository at this point in the history
…#65463)

For debugging purposes, it can be useful to set `NODE_ENV=development`
during a `next build`. Currently this value is forced to be production
in Next.js. This PR adds an experimental flag to not force a mode of
`production` when the flag is set.

To use this flag, you'll still need to explicitly set
`NODE_ENV=development`, while also enabling
`nextConfig.experimental.allowDevelopmentBuild`

Closes NEXT-3277
  • Loading branch information
ztanner committed May 7, 2024
1 parent d0d22ac commit 8b9aa2d
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 2 deletions.
4 changes: 3 additions & 1 deletion packages/next/src/build/webpack/loaders/next-swc-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ async function loaderTransform(
filename,
isServer,
isPageFile,
development: this.mode === 'development',
development:
this.mode === 'development' ||
!!nextConfig.experimental?.allowDevelopmentBuild,
hasReactRefresh,
modularizeImports: nextConfig?.modularizeImports,
optimizePackageImports: nextConfig?.experimental?.optimizePackageImports,
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/build/webpack/plugins/define-env-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ export function getDefineEnv({
'process.turbopack': isTurbopack,
'process.env.TURBOPACK': isTurbopack,
// TODO: enforce `NODE_ENV` on `process.env`, and add a test:
'process.env.NODE_ENV': dev ? 'development' : 'production',
'process.env.NODE_ENV':
dev || config.experimental.allowDevelopmentBuild
? 'development'
: 'production',
'process.env.NEXT_RUNTIME': isEdgeServer
? 'edge'
: isNodeServer
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ export const configSchema: zod.ZodType<NextConfig> = z.lazy(() =>
useEarlyImport: z.boolean().optional(),
testProxy: z.boolean().optional(),
defaultTestRunner: z.enum(SUPPORTED_TEST_RUNNERS_LIST).optional(),
allowDevelopmentBuild: z.literal(true).optional(),
})
.optional(),
exportPathMap: z
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,10 @@ export interface ExperimentalConfig {
* Set a default test runner to be used by `next experimental-test`.
*/
defaultTestRunner?: SupportedTestRunners
/**
* Allow NODE_ENV=development even for `next build`.
*/
allowDevelopmentBuild?: true
}

export type ExportPathMap = {
Expand Down Expand Up @@ -963,6 +967,7 @@ export const defaultConfig: NextConfig = {
dynamic: 30,
static: 300,
},
allowDevelopmentBuild: undefined,
},
bundlePagesRouterDependencies: false,
}
Expand Down
9 changes: 9 additions & 0 deletions packages/next/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,15 @@ function assignDefaults(

const result = { ...defaultConfig, ...config }

if (
result.experimental?.allowDevelopmentBuild &&
process.env.NODE_ENV !== 'development'
) {
throw new Error(
`The experimental.allowDevelopmentBuild option requires NODE_ENV to be explicitly set to 'development'.`
)
}

if (
result.experimental?.ppr &&
!process.env.__NEXT_VERSION!.includes('canary') &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('allow-development-build', () => {
describe('with NODE_ENV set to development', () => {
const { next } = nextTestSetup({
files: __dirname,
env: {
NODE_ENV: 'development',
},
nextConfig: {
experimental: {
allowDevelopmentBuild: true,
},
},
})

it('should warn about a non-standard NODE_ENV', () => {
expect(next.cliOutput).toContain(
'You are using a non-standard "NODE_ENV" value in your environment'
)
})

it.each(['app-page', 'pages-page'])(
`should show React development errors in %s`,
async (page) => {
const browser = await next.browser(page, {
pushErrorAsConsoleLog: true,
})

await retry(async () => {
const logs = await browser.log()

const errorLogs = logs.filter((log) => log.source === 'error')

expect(errorLogs).toEqual(
expect.arrayContaining([
{
message: expect.toBeOneOf([
expect.stringContaining(
"Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client."
),
expect.stringContaining(
'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.'
),
]),
source: 'error',
},
])
)
})
}
)
})

describe('with NODE_ENV not set to development', () => {
const { next } = nextTestSetup({
files: __dirname,
skipStart: true,
nextConfig: {
experimental: {
allowDevelopmentBuild: true,
},
},
})

it('should fail the build with a message about not setting NODE_ENV', async () => {
await next.start().catch(() => {})
expect(next.cliOutput).toContain(
"The experimental.allowDevelopmentBuild option requires NODE_ENV to be explicitly set to 'development'"
)
})
})
})
11 changes: 11 additions & 0 deletions test/production/allow-development-build/app/app-page/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'
import React from 'react'

export default function Page() {
return (
<div>
Hello World{' '}
{typeof window !== 'undefined' && <span>Hydration Error!</span>}
</div>
)
}
11 changes: 11 additions & 0 deletions test/production/allow-development-build/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
8 changes: 8 additions & 0 deletions test/production/allow-development-build/pages/pages-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function Page() {
return (
<div>
Hello World{' '}
{typeof window !== 'undefined' && <span>Hydration Error!</span>}
</div>
)
}

0 comments on commit 8b9aa2d

Please sign in to comment.