diff --git a/packages/next/src/build/flying-shuttle/inline-static-env.ts b/packages/next/src/build/flying-shuttle/inline-static-env.ts index 84a13b9fff1ef..bb9ddebf04f06 100644 --- a/packages/next/src/build/flying-shuttle/inline-static-env.ts +++ b/packages/next/src/build/flying-shuttle/inline-static-env.ts @@ -2,13 +2,28 @@ import fs from 'fs' import path from 'path' import { promisify } from 'util' import globOriginal from 'next/dist/compiled/glob' -import { getNextPublicEnvironmentVariables } from '../webpack/plugins/define-env-plugin' +import { + getNextConfigEnv, + getNextPublicEnvironmentVariables, +} from '../webpack/plugins/define-env-plugin' import { Sema } from 'next/dist/compiled/async-sema' +import type { NextConfigComplete } from '../../server/config-shared' const glob = promisify(globOriginal) -export async function inlineStaticEnv({ distDir }: { distDir: string }) { - const staticEnv = getNextPublicEnvironmentVariables() +export async function inlineStaticEnv({ + distDir, + config, +}: { + distDir: string + config: NextConfigComplete +}) { + const nextConfigEnv = getNextConfigEnv(config) + + const staticEnv = { + ...getNextPublicEnvironmentVariables(), + ...nextConfigEnv, + } const serverDir = path.join(distDir, 'server') const serverChunks = await glob('**/*.js', { @@ -20,6 +35,14 @@ export async function inlineStaticEnv({ distDir }: { distDir: string }) { }) const inlineSema = new Sema(8) + const nextConfigEnvKeys = Object.keys(nextConfigEnv).map((item) => + item.split('process.env.').pop() + ) + + const builtRegEx = new RegExp( + `[\\w]{1,}\\.env\\.(?:NEXT_PUBLIC_[\\w]{1,}${nextConfigEnvKeys.length ? '|' + nextConfigEnvKeys.join('|') : ''})`, + 'g' + ) for (const [parentDir, files] of [ [serverDir, serverChunks], @@ -33,7 +56,7 @@ export async function inlineStaticEnv({ distDir }: { distDir: string }) { await fs.promises.writeFile( filepath, - content.replace(/[\w]{1,}\.env\.NEXT_PUBLIC_[\w]{1,}/g, (match) => { + content.replace(builtRegEx, (match) => { let normalizedMatch = `process.env.${match.split('.').pop()}` if (staticEnv[normalizedMatch]) { diff --git a/packages/next/src/build/flying-shuttle/store-shuttle.ts b/packages/next/src/build/flying-shuttle/store-shuttle.ts index 9165a658c4d82..8faa93fcd3389 100644 --- a/packages/next/src/build/flying-shuttle/store-shuttle.ts +++ b/packages/next/src/build/flying-shuttle/store-shuttle.ts @@ -30,7 +30,6 @@ export function generateShuttleManifest(config: NextConfigComplete) { gitSha, nextVersion, config: { - env: config.env, i18n: config.i18n, basePath: config.basePath, sassOptions: config.sassOptions, diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 04d908262a1b9..5a748df40a0c1 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -2670,7 +2670,7 @@ export default async function build( await nextBuildSpan .traceChild('inline-static-env') .traceAsyncFn(async () => { - await inlineStaticEnv({ distDir }) + await inlineStaticEnv({ config, distDir }) }) } } diff --git a/packages/next/src/build/webpack/plugins/define-env-plugin.ts b/packages/next/src/build/webpack/plugins/define-env-plugin.ts index 54efea18085a4..854c788193f03 100644 --- a/packages/next/src/build/webpack/plugins/define-env-plugin.ts +++ b/packages/next/src/build/webpack/plugins/define-env-plugin.ts @@ -74,7 +74,7 @@ export function getNextPublicEnvironmentVariables(): DefineEnv { /** * Collects the `env` config value from the Next.js config. */ -function getNextConfigEnv(config: NextConfigComplete): DefineEnv { +export function getNextConfigEnv(config: NextConfigComplete): DefineEnv { // Refactored code below to use for-of const defineEnv: DefineEnv = {} const env = config.env @@ -286,9 +286,10 @@ export function getDefineEnv({ // with flying shuttle enabled so we can update them // without invalidating entries for (const key in nextPublicEnv) { - if (key in nextConfigEnv) { - continue - } + serializedDefineEnv[key] = key + } + + for (const key in nextConfigEnv) { serializedDefineEnv[key] = key } } diff --git a/test/e2e/app-dir/app/app/legacy-env/page.js b/test/e2e/app-dir/app/app/legacy-env/page.js new file mode 100644 index 0000000000000..91abe39167181 --- /dev/null +++ b/test/e2e/app-dir/app/app/legacy-env/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return
{process.env.LEGACY_ENV_KEY}
+} diff --git a/test/e2e/app-dir/app/flying-shuttle.test.ts b/test/e2e/app-dir/app/flying-shuttle.test.ts index 86ad5500bd803..9a379b04e8c84 100644 --- a/test/e2e/app-dir/app/flying-shuttle.test.ts +++ b/test/e2e/app-dir/app/flying-shuttle.test.ts @@ -1,5 +1,6 @@ import fs from 'fs' import path from 'path' +import cheerio from 'cheerio' import { version as nextVersion } from 'next/package.json' import type { Route } from 'playwright' import { retry } from 'next-test-utils' @@ -423,5 +424,32 @@ import { nextTestSetup, isNextStart } from 'e2e-utils' await next.patchFile(dataPath, originalDataContent) } }) + + it('should not invalidate on legacy next env but inline properly', async () => { + await next.stop() + + const dataPath = 'next.config.js' + const originalDataContent = await next.readFile(dataPath) + + try { + await next.patchFile( + dataPath, + originalDataContent.replace( + `LEGACY_ENV_KEY: '1'`, + `LEGACY_ENV_KEY: '2'` + ) + ) + await nextStart() + + const res = await next.fetch('/legacy-env') + const html = await res.text() + const $ = cheerio.load(html) + + expect(res.status).toBe(200) + expect($('#legacy-env').text()).toBe('2') + } finally { + await next.patchFile(dataPath, originalDataContent) + } + }) } ) diff --git a/test/e2e/app-dir/app/next.config.js b/test/e2e/app-dir/app/next.config.js index 0cb6ced06bdf3..7b40a18ade84a 100644 --- a/test/e2e/app-dir/app/next.config.js +++ b/test/e2e/app-dir/app/next.config.js @@ -2,6 +2,9 @@ * @type import('next').NextConfig */ module.exports = { + env: { + LEGACY_ENV_KEY: '1', + }, experimental: { clientRouterFilterRedirects: true, parallelServerCompiles: true,