mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
### What?
Adds a new `chunkLoadingGlobal` option to the `turbopack` config in
`next.config.js` that overrides the global variable name used for
Turbopack chunk loading (defaults to `TURBOPACK`).
```js
// next.config.js
module.exports = {
turbopack: {
chunkLoadingGlobal: 'myApp',
},
}
```
### Why?
When multiple Turbopack-built apps run on the same page (e.g. horizontal
micro-frontends), they each try to use `globalThis.TURBOPACK` which
causes conflicts. This option allows each app to use its own isolated
global, mirroring webpack's `output.chunkLoadingGlobal`.
The `chunk_loading_global` method was already wired into Turbopack in
#88790 — this PR exposes it via Next.js config.
A reason is
[vercel/remote-components#239](https://github.com/vercel/remote-components/pull/239),
which uses this option to eliminate four brittle `globalThis.TURBOPACK`
regex rename transforms in the remote-components runtime. Those
transforms target Turbopack's internal variable naming and have already
required updates as Turbopack's output format evolved. With
`chunkLoadingGlobal` set at build time, Turbopack handles the renaming
natively and those transforms become unnecessary.
### How?
- `config-shared.ts` / `config-schema.ts` — adds `chunkLoadingGlobal?:
string` to `TurbopackOptions` with Zod validation
- `next_config.rs` — adds the field to `TurbopackConfig` and a
`turbopack_chunk_loading_global()` accessor
- `next_client/context.rs` — threads it through
`ClientChunkingContextOptions` and calls
`builder.chunk_loading_global()` when set
- `project.rs` — wires the config into the client chunking context
options
Only applies to the browser/client chunking context — server and edge
runtimes don't have this concept.
### Test plan
- `pnpm test-start-turbo
test/production/app-dir/turbopack-chunk-loading-global/turbopack-chunk-loading-global.test.ts`
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Niklas Mischkulnig <[email protected]>
58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
import fs from 'fs'
|
|
import path from 'path'
|
|
import { nextTestSetup } from 'e2e-utils'
|
|
|
|
function collectJsFiles(dir: string): string[] {
|
|
if (!fs.existsSync(dir)) return []
|
|
return fs
|
|
.readdirSync(dir, { recursive: true, encoding: 'utf8' })
|
|
.flatMap((entry) => {
|
|
const full = path.join(dir, entry)
|
|
return entry.endsWith('.js') && fs.statSync(full).isFile() ? [full] : []
|
|
})
|
|
}
|
|
|
|
describe('turbopack-chunk-loading-global', () => {
|
|
const { next, isNextDeploy, isTurbopack } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
;(isNextDeploy || !isTurbopack ? describe.skip : describe)(
|
|
'chunk output',
|
|
() => {
|
|
it('uses the custom global name and drops the default TURBOPACK global', async () => {
|
|
const staticDir = path.join(next.testDir, '.next/static')
|
|
const jsFiles = collectJsFiles(staticDir)
|
|
expect(jsFiles.length).toBeGreaterThan(0)
|
|
|
|
const allContent = jsFiles
|
|
.map((f) => fs.readFileSync(f, 'utf8'))
|
|
.join('\n')
|
|
|
|
// Custom global must be used for chunk loading (auto-prefixed with TURBOPACK_)
|
|
expect(allContent).toContain('globalThis.TURBOPACK_myApp')
|
|
|
|
// Bare default TURBOPACK global must not appear — if it does,
|
|
// chunkLoadingGlobal was ignored by the Rust layer
|
|
expect(allContent).not.toMatch(/globalThis\.TURBOPACK(?!_)/)
|
|
})
|
|
}
|
|
)
|
|
|
|
describe('runtime behavior', () => {
|
|
it('renders content and handles interactions after hydration', async () => {
|
|
const browser = await next.browser('/')
|
|
|
|
// Initial render
|
|
expect(await browser.elementByCss('#count').text()).toBe('0')
|
|
|
|
// Click increments — proves chunks loaded and JS executed correctly
|
|
await browser.elementByCss('button').click()
|
|
expect(await browser.elementByCss('#count').text()).toBe('1')
|
|
|
|
await browser.elementByCss('button').click()
|
|
expect(await browser.elementByCss('#count').text()).toBe('2')
|
|
})
|
|
})
|
|
})
|