Skip to content

Commit 9216ab1

Browse files
authored
[dynamicIO] routes with dynamic segments should be able to be static in dev (#76691)
Dev mode reports static/dynamic behavior using a heuristic when dynamicIO is enabled. The heuristic previously incorrectly marked routes as dynamic when they were in fact static as a consequence of refactoring metadata to support streaming. This update adjust the heuristic to not incorrectly assume dynamic in these situations. The root cause is that errors in `onError` cannot be assumed to be valid if the render has already aborted Closes NAR-112
1 parent 6f6001a commit 9216ab1

File tree

5 files changed

+21
-20
lines changed

5 files changed

+21
-20
lines changed

packages/next/src/export/index.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,7 @@ async function exportAppImpl(
387387
strictNextHead: nextConfig.experimental.strictNextHead ?? true,
388388
deploymentId: nextConfig.deploymentId,
389389
htmlLimitedBots: nextConfig.htmlLimitedBots.source,
390-
streamingMetadata:
391-
// Disable streaming metadata when dynamic IO is enabled.
392-
// FIXME: remove dynamic IO guard once we fixed the dynamic indicator case.
393-
// test/e2e/app-dir/dynamic-io/dynamic-io.test.ts - should not have static indicator on not-found route
394-
!nextConfig.experimental.dynamicIO,
390+
streamingMetadata: true,
395391
experimental: {
396392
clientTraceMetadata: nextConfig.experimental.clientTraceMetadata,
397393
expireTime: nextConfig.expireTime,

packages/next/src/export/worker.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,7 @@ export async function exportPages(
425425
enableExperimentalReact: needsExperimentalReact(nextConfig),
426426
sriEnabled: Boolean(nextConfig.experimental.sri?.algorithm),
427427
buildId: input.buildId,
428-
streamingMetadata:
429-
// Disable streaming metadata when dynamic IO is enabled.
430-
// FIXME: remove dynamic IO guard once we fixed the dynamic indicator case.
431-
// test/e2e/app-dir/dynamic-io/dynamic-io.test.ts - should not have static indicator on not-found route
432-
!nextConfig.experimental.dynamicIO,
428+
streamingMetadata: true,
433429
}),
434430
// If exporting the page takes longer than the timeout, reject the promise.
435431
new Promise((_, reject) => {

packages/next/src/server/app-render/app-render.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -2446,6 +2446,7 @@ async function spawnDynamicValidationInDev(
24462446
}
24472447
)
24482448

2449+
let rootDidError = false
24492450
const serverPhasedStream = serverPrerenderStreamResult.asPhasedStream()
24502451
try {
24512452
const prerender = require('react-dom/static.edge')
@@ -2476,7 +2477,12 @@ async function spawnDynamicValidationInDev(
24762477
isPrerenderInterruptedError(err) ||
24772478
finalClientController.signal.aborted
24782479
) {
2479-
requestStore.usedDynamic = true
2480+
if (!rootDidError) {
2481+
// If the root errored before we observe this error then it wasn't caused by something dynamic.
2482+
// If the root did not error or is erroring because of a sync dynamic API or a prerender interrupt error
2483+
// then we are a dynamic route.
2484+
requestStore.usedDynamic = true
2485+
}
24802486

24812487
const componentStack = errorInfo.componentStack
24822488
if (typeof componentStack === 'string') {
@@ -2501,6 +2507,7 @@ async function spawnDynamicValidationInDev(
25012507
}
25022508
)
25032509
} catch (err) {
2510+
rootDidError = true
25042511
if (
25052512
isPrerenderInterruptedError(err) ||
25062513
finalClientController.signal.aborted

packages/next/src/server/base-server.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -600,11 +600,7 @@ export default abstract class Server<
600600
isExperimentalCompile: this.nextConfig.experimental.isExperimentalCompile,
601601
// `htmlLimitedBots` is passed to server as serialized config in string format
602602
htmlLimitedBots: this.nextConfig.htmlLimitedBots,
603-
streamingMetadata:
604-
// Disable streaming metadata when dynamic IO is enabled.
605-
// FIXME: remove dynamic IO guard once we fixed the dynamic indicator case.
606-
// test/e2e/app-dir/dynamic-io/dynamic-io.test.ts - should not have static indicator on not-found route
607-
!this.nextConfig.experimental.dynamicIO,
603+
streamingMetadata: true,
608604
experimental: {
609605
expireTime: this.nextConfig.expireTime,
610606
clientTraceMetadata: this.nextConfig.experimental.clientTraceMetadata,

test/e2e/app-dir/dynamic-io-errors/dynamic-io-errors.test.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,16 @@ function runTests(options: { withMinification: boolean }) {
159159
throw new Error('expected build not to fail for fully static project')
160160
}
161161

162-
expect(next.cliOutput).toContain('ƒ / ')
163-
const $ = await next.render$('/')
164-
expect($('#dynamic').text()).toBe('Dynamic')
165-
expect($('[data-fallback]').length).toBe(0)
162+
if (WITH_PPR) {
163+
expect(next.cliOutput).toContain('◐ / ')
164+
const $ = await next.render$('/')
165+
expect($('#dynamic').text()).toBe('Dynamic')
166+
expect($('[data-fallback]').length).toBe(1)
167+
} else {
168+
expect(next.cliOutput).toContain('ƒ / ')
169+
const $ = await next.render$('/')
170+
expect($('#dynamic').text()).toBe('Dynamic')
171+
}
166172
})
167173
})
168174

0 commit comments

Comments
 (0)