Files
Tim NeutkensandGitHub 3a48dd6a6c Optimize app page entry analysis (#96058)
## Summary

- Extract the invariant app-page handler into a shared runtime module
while keeping each generated route entry as a small wrapper.
- Keep the `interopDefault` binding in the route wrapper because the
injected Turbopack metadata loader tree references it, and pass the same
binding into the shared runtime.
- Add a focused 14-route navigation benchmark with static metadata
coverage.

## Why

Turbopack gives every generated app-page entry a route-specific identity
because its loader tree, page name, pathname, and module hooks are
injected into the template. This caused the full app-page handler to be
analyzed independently for every route.

A clean native trace of the benchmark shows app-page analysis CPU
falling from 1,619.39 ms across 14 route copies to 153.92 ms total
across 14 small wrappers plus one shared runtime, a 90.5% reduction.
Concurrency-corrected analysis time falls from 181.63 ms to 97.87 ms.
The post-change fixture also includes a static icon to cover the
injected metadata loader binding.

## Verification

- `pnpm test-dev-turbo
test/e2e/app-dir/metadata-icons/metadata-icons.test.ts`
- `pnpm test-start-turbo
test/production/adapter-config/adapter-config-export.test.ts`
- `pnpm test-dev-turbo
test/development/app-dir/app-page-entry-prefetch-performance/app-page-entry-prefetch-performance.test.ts`
- `pnpm test-dev-webpack
test/development/app-dir/app-page-entry-prefetch-performance/app-page-entry-prefetch-performance.test.ts`
- Clean native reproduction: the metadata suite failed with
`ReferenceError: interopDefault is not defined` before the binding fix
and passed all 9 cases afterward.

<!-- NEXT_JS_LLM -->
2026-07-24 11:55:49 +02:00

92 lines
2.6 KiB
TypeScript

import { nextTestSetup } from 'e2e-utils'
const backgroundRoutes = [
'/contacts',
'/emails',
'/templates',
'/workflows',
'/metrics',
'/logs',
'/api-keys',
'/domain',
'/webhooks',
'/integrations',
'/smtp',
'/settings',
]
describe('app-page entry prefetch performance', () => {
const traceEnv = process.env.NEXT_TURBOPACK_TRACING
? {
NEXT_TURBOPACK_TRACING: process.env.NEXT_TURBOPACK_TRACING,
...(process.env.NEXT_TURBOPACK_TRACING_PATH
? {
NEXT_TURBOPACK_TRACING_PATH:
process.env.NEXT_TURBOPACK_TRACING_PATH,
}
: {}),
}
: {}
const { next, isTurbopack } = nextTestSetup({
files: __dirname,
env: traceEnv,
skipStart: true,
})
beforeAll(async () => {
await next.start()
})
if (!isTurbopack) {
it('renders the route-specific app-page entry', async () => {
const $ = await next.render$('/dashboard/agent-inbox')
expect($('#agent-inbox').text()).toBe('agent-inbox')
})
it.skip('requires Turbopack tracing', () => {})
return
}
it('navigates to agent inbox with many visible route prefetches', async () => {
const browser = await next.browser('/dashboard')
expect(
await browser.eval(`document.querySelectorAll('nav a').length`)
).toBe(14)
expect(await browser.elementByCss('#overview').text()).toBe('overview')
expect(
await browser.eval(`document.querySelectorAll('link[rel="icon"]').length`)
).toBe(1)
// Link viewport prefetches are production-only in this checkout. Start the
// same endpoint compilation batch explicitly so the development benchmark
// deterministically models the source application's partial prefetching.
const batchStart = performance.now()
const backgroundCompilations = Promise.all(
backgroundRoutes.map(async (pathname) => {
const response = await next.fetch(`/dashboard${pathname}`)
expect(response.status).toBe(200)
await response.arrayBuffer()
})
)
await browser.eval(`window.__navigationStart = performance.now()`)
await browser.elementByCss('a[href="/dashboard/agent-inbox"]').click()
await browser.waitForElementByCss('#agent-inbox')
const elapsed = await browser.eval(
`performance.now() - window.__navigationStart`
)
await backgroundCompilations
const batchElapsed = performance.now() - batchStart
console.log(
`cold agent-inbox navigation under route compilation load: ${elapsed.toFixed(2)} ms`
)
console.log(
`12-route app-page compilation batch: ${batchElapsed.toFixed(2)} ms`
)
})
})