Skip to content

Commit 15af7f4

Browse files
authored
Add env handling for experimental tracing (#68516)
This moves the NEXT_PUBLIC env inlining step out of the webpack compilation so that the experimental tracing doesn't need to invalidate based on these values allowing better cache re-use. Closes: NDX-127
1 parent 4b688c9 commit 15af7f4

File tree

6 files changed

+92
-3
lines changed

6 files changed

+92
-3
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
import { promisify } from 'util'
4+
import globOriginal from 'next/dist/compiled/glob'
5+
import { getNextPublicEnvironmentVariables } from '../webpack/plugins/define-env-plugin'
6+
import { Sema } from 'next/dist/compiled/async-sema'
7+
8+
const glob = promisify(globOriginal)
9+
10+
export async function inlineStaticEnv({ distDir }: { distDir: string }) {
11+
const staticEnv = getNextPublicEnvironmentVariables()
12+
13+
const serverDir = path.join(distDir, 'server')
14+
const serverChunks = await glob('**/*.js', {
15+
cwd: serverDir,
16+
})
17+
const clientDir = path.join(distDir, 'static')
18+
const clientChunks = await glob('**/*.js', {
19+
cwd: clientDir,
20+
})
21+
22+
const inlineSema = new Sema(8)
23+
24+
for (const [parentDir, files] of [
25+
[serverDir, serverChunks],
26+
[clientDir, clientChunks],
27+
] as const) {
28+
await Promise.all(
29+
files.map(async (file) => {
30+
await inlineSema.acquire()
31+
const filepath = path.join(parentDir, file)
32+
const content = await fs.promises.readFile(filepath, 'utf8')
33+
// TODO: should we support process.env['NEXT_PUBLIC_KEY'] format?
34+
await fs.promises.writeFile(
35+
filepath,
36+
content.replace(/['"`]?[\w]{1,}\.env\.[\w]{1,}['"`]?/g, (match) => {
37+
const matchParts = match.split('.')
38+
let normalizedMatch = `process.env.${matchParts[2].replace(
39+
// remove ending quote if present
40+
/['"`]$/,
41+
''
42+
)}`
43+
if (staticEnv[normalizedMatch]) {
44+
return JSON.stringify(staticEnv[normalizedMatch])
45+
}
46+
return match
47+
})
48+
)
49+
inlineSema.release()
50+
})
51+
)
52+
}
53+
}

packages/next/src/build/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ import {
194194
} from './flying-shuttle/detect-changed-entries'
195195
import { storeShuttle } from './flying-shuttle/store-shuttle'
196196
import { stitchBuilds } from './flying-shuttle/stitch-builds'
197+
import { inlineStaticEnv } from './flying-shuttle/inline-static-env'
197198

198199
interface ExperimentalBypassForInfo {
199200
experimentalBypassFor?: RouteHas[]
@@ -2543,6 +2544,9 @@ export default async function build(
25432544
distDir,
25442545
shuttleDir,
25452546
})
2547+
2548+
console.log('inlining static env')
2549+
await inlineStaticEnv({ distDir })
25462550
}
25472551
}
25482552

packages/next/src/build/webpack/plugins/define-env-plugin.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,24 @@ export function getDefineEnv({
139139
isNodeServer,
140140
middlewareMatchers,
141141
}: DefineEnvPluginOptions): SerializedDefineEnv {
142+
const nextPublicEnv = getNextPublicEnvironmentVariables()
143+
144+
if (config.experimental.flyingShuttle) {
145+
// we delay inlining these values until after the build
146+
// with flying shuttle enabled so we can update them
147+
// without invalidating entries
148+
for (const key in nextPublicEnv) {
149+
// we inline as the key itself to avoid process.env
150+
// mangling by webpack/minifier making replacing unsafe
151+
nextPublicEnv[key] = key
152+
}
153+
}
154+
142155
const defineEnv: DefineEnv = {
143156
// internal field to identify the plugin config
144157
__NEXT_DEFINE_ENV: true,
145158

146-
...getNextPublicEnvironmentVariables(),
159+
...nextPublicEnv,
147160
...getNextConfigEnv(config),
148161
...(!isEdgeServer
149162
? {}

test/e2e/app-dir/app/app/dashboard/deployments/[id]/page.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default function DeploymentsPage(props) {
2222
return (
2323
<>
2424
<p>hello from app/dashboard/deployments/[id]. ID is: {data.id}</p>
25+
<p id="my-env">{process.env.NEXT_PUBLIC_TEST_ID}</p>
2526
</>
2627
)
2728
}

test/e2e/app-dir/app/flying-shuttle.test.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,22 @@ import { nextTestSetup, isNextStart } from 'e2e-utils'
1313
it('should skip for non-next start', () => {})
1414
return
1515
}
16-
1716
const { next } = nextTestSetup({
1817
files: __dirname,
1918
dependencies: {
2019
nanoid: '4.0.1',
2120
},
2221
env: {
23-
NEXT_PRIVATE_FLYING_SHUTTLE: 'true',
22+
NEXT_PRIVATE_FLYING_SHUTTLE: '1',
2423
},
2524
})
2625
let initialConfig: Record<string, any> = {}
26+
let testEnvId = ''
27+
28+
beforeEach(() => {
29+
testEnvId = Math.random() + ''
30+
next.env['NEXT_PUBLIC_TEST_ID'] = testEnvId
31+
})
2732

2833
beforeAll(async () => {
2934
const manifest = await next.readJSON(
@@ -123,6 +128,12 @@ import { nextTestSetup, isNextStart } from 'e2e-utils'
123128
})
124129

125130
async function checkAppPagesNavigation() {
131+
// ensure we inlined NEXT_PUBLIC_ properly
132+
const index$ = await next.render$('/')
133+
const deployments$ = await next.render$('/dashboard/deployments/123')
134+
expect(index$('#my-env').text()).toContain(testEnvId)
135+
expect(deployments$('#my-env').text()).toContain(testEnvId)
136+
126137
const testPaths = [
127138
{ path: '/', content: 'hello from pages/index', type: 'pages' },
128139
{
@@ -163,6 +174,12 @@ import { nextTestSetup, isNextStart } from 'e2e-utils'
163174
).toContain(content)
164175
})
165176

177+
if (path === '/' || path === '/dashboard/deployments/123') {
178+
expect(await browser.elementByCss('#my-env').text()).toContain(
179+
testEnvId
180+
)
181+
}
182+
166183
const checkNav = async (testPath: (typeof testPaths)[0]) => {
167184
await browser.eval(`window.next.router.push("${testPath.path}")`)
168185

test/e2e/app-dir/app/pages/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default function Page() {
1616
<Link href="/dashboard">Dashboard</Link>
1717
<p id="react-version">{React.version}</p>
1818
<Button>Click me!</Button>
19+
<p id="my-env">{process.env.NEXT_PUBLIC_TEST_ID}</p>
1920
</>
2021
)
2122
}

0 commit comments

Comments
 (0)