Skip to content

Commit d04d65b

Browse files
authored
[segment explorer] fix route url is missing cases (#81622)
1 parent 1758c3c commit d04d65b

File tree

6 files changed

+70
-25
lines changed

6 files changed

+70
-25
lines changed

packages/next/src/client/components/layout-router.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { createRouterCacheKey } from './router-reducer/create-router-cache-key'
4040
import { hasInterceptionRouteInCurrentTree } from './router-reducer/reducers/has-interception-route-in-current-tree'
4141
import { dispatchAppRouterAction } from './use-action-queue'
4242
import { useRouterBFCache, type RouterBFCacheEntry } from './bfcache'
43+
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'
4344

4445
const Activity = process.env.__NEXT_ROUTER_BF_CACHE
4546
? (require('react') as typeof import('react')).unstable_Activity
@@ -616,13 +617,19 @@ export default function OuterLayoutRouter({
616617
: ErrorBoundary
617618

618619
let segmentBoundaryTriggerNode: React.ReactNode = null
620+
let segmentViewStateNode: React.ReactNode = null
619621
if (
620622
process.env.NODE_ENV !== 'production' &&
621623
process.env.__NEXT_DEVTOOL_SEGMENT_EXPLORER
622624
) {
623-
const { SegmentBoundaryTriggerNode } =
625+
const { SegmentBoundaryTriggerNode, SegmentViewStateNode } =
624626
require('../../next-devtools/userspace/app/segment-explorer-node') as typeof import('../../next-devtools/userspace/app/segment-explorer-node')
625627

628+
const pagePrefix = normalizeAppPath(url)
629+
segmentViewStateNode = (
630+
<SegmentViewStateNode key={pagePrefix} page={pagePrefix} />
631+
)
632+
626633
segmentBoundaryTriggerNode = (
627634
<>
628635
<SegmentBoundaryTriggerNode />
@@ -667,6 +674,7 @@ export default function OuterLayoutRouter({
667674
</HTTPAccessFallbackBoundary>
668675
</LoadingBoundary>
669676
</ErrorBoundaryComponent>
677+
{segmentViewStateNode}
670678
</ScrollAndFocusHandler>
671679
}
672680
>

packages/next/src/server/app-render/create-component-tree.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ async function createComponentTreeInternal({
105105
workStore,
106106
componentMod: {
107107
SegmentViewNode,
108-
SegmentViewStateNode,
109108
HTTPAccessFallbackBoundary,
110109
LayoutRouter,
111110
RenderFromTemplateContext,
@@ -800,7 +799,6 @@ async function createComponentTreeInternal({
800799
pageElement
801800
)
802801

803-
const pagePrefix = ctx.renderOpts.page.replace(/\/page$/, '')
804802
return [
805803
actualSegment,
806804
<React.Fragment key={cacheNodeKey}>
@@ -810,7 +808,6 @@ async function createComponentTreeInternal({
810808
<MetadataOutlet ready={getViewportReady} />
811809
{metadataOutlet}
812810
</OutletBoundary>
813-
<SegmentViewStateNode page={pagePrefix} />
814811
</React.Fragment>,
815812
parallelRouteCacheNodeSeedData,
816813
loadingData,

test/development/app-dir/segment-explorer/segment-explorer.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { nextTestSetup } from 'e2e-utils'
2-
import { getSegmentExplorerContent, retry } from 'next-test-utils'
2+
import {
3+
getSegmentExplorerContent,
4+
getSegmentExplorerRoute,
5+
retry,
6+
} from 'next-test-utils'
37

48
describe('segment-explorer', () => {
59
const { next } = nextTestSetup({
@@ -14,6 +18,7 @@ describe('segment-explorer', () => {
1418
@bar/ [layout.tsx, page.tsx]
1519
@foo/ [layout.tsx, page.tsx]"
1620
`)
21+
expect(await getSegmentExplorerRoute(browser)).toBe('/parallel-routes')
1722
})
1823

1924
it('should render the segment explorer for parallel routes in edge runtime', async () => {
@@ -24,6 +29,7 @@ describe('segment-explorer', () => {
2429
@bar/ [layout.tsx, page.tsx]
2530
@foo/ [layout.tsx, page.tsx]"
2631
`)
32+
expect(await getSegmentExplorerRoute(browser)).toBe('/parallel-routes-edge')
2733
})
2834

2935
it('should render the segment explorer for nested routes', async () => {
@@ -35,6 +41,7 @@ describe('segment-explorer', () => {
3541
~ / (overview)/ [layout.tsx]
3642
grid/ [page.tsx]"
3743
`)
44+
expect(await getSegmentExplorerRoute(browser)).toBe('/blog/~/grid')
3845
})
3946

4047
it('should cleanup on soft navigation', async () => {
@@ -43,6 +50,7 @@ describe('segment-explorer', () => {
4350
"app/ [layout.tsx]
4451
soft-navigation / a/ [page.tsx]"
4552
`)
53+
expect(await getSegmentExplorerRoute(browser)).toBe('/soft-navigation/a')
4654

4755
await browser.elementByCss('[href="/soft-navigation/b"]').click()
4856
await retry(async () => {
@@ -53,6 +61,7 @@ describe('segment-explorer', () => {
5361
"app/ [layout.tsx]
5462
soft-navigation / b/ [page.tsx]"
5563
`)
64+
expect(await getSegmentExplorerRoute(browser)).toBe('/soft-navigation/b')
5665
})
5766

5867
it('should handle show file segments in order', async () => {
@@ -61,6 +70,7 @@ describe('segment-explorer', () => {
6170
"app/ [layout.tsx]
6271
(all) / file-segments/ [layout.tsx, template.tsx, page.tsx]"
6372
`)
73+
expect(await getSegmentExplorerRoute(browser)).toBe('/file-segments')
6474
})
6575

6676
it('should indicate segment explorer is not available for pages router', async () => {
@@ -73,13 +83,16 @@ describe('segment-explorer', () => {
7383
expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(
7484
`"app/ [layout.tsx, not-found.js]"`
7585
)
86+
expect(await getSegmentExplorerRoute(browser)).toBe('/404')
7687
})
7788

7889
it('should show global-error segment', async () => {
7990
const browser = await next.browser('/runtime-error')
8091
expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(
8192
`"app/ [global-error.js]"`
8293
)
94+
// FIXME: handle preserve the url when hitting global-error
95+
expect(await getSegmentExplorerRoute(browser)).toBe('')
8396
})
8497

8598
it('should show navigation boundaries of the segment', async () => {
@@ -88,6 +101,9 @@ describe('segment-explorer', () => {
88101
"app/ [layout.tsx]
89102
boundary/ [layout.tsx, not-found.tsx]"
90103
`)
104+
expect(await getSegmentExplorerRoute(browser)).toBe(
105+
'/boundary?name=not-found'
106+
)
91107

92108
await browser.loadPage(`${next.url}/boundary?name=forbidden`)
93109
expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(`
@@ -116,6 +132,7 @@ describe('segment-explorer', () => {
116132
"app/ [layout.tsx]
117133
search/ [layout.tsx, loading.tsx]"
118134
`)
135+
expect(await getSegmentExplorerRoute(browser)).toBe('/search?q=abc')
119136
})
120137

121138
it('should show the custom error boundary when present', async () => {
@@ -124,6 +141,9 @@ describe('segment-explorer', () => {
124141
"app/ [layout.tsx]
125142
runtime-error / boundary/ [error.tsx]"
126143
`)
144+
expect(await getSegmentExplorerRoute(browser)).toBe(
145+
'/runtime-error/boundary'
146+
)
127147
})
128148

129149
it('should display parallel routes default page when present', async () => {
@@ -135,6 +155,9 @@ describe('segment-explorer', () => {
135155
subroute/ [page.tsx]
136156
@foo/ [default.tsx]"
137157
`)
158+
expect(await getSegmentExplorerRoute(browser)).toBe(
159+
'/parallel-default/subroute'
160+
)
138161
})
139162

140163
it('should display boundary selector when a segment has only boundary files', async () => {
@@ -145,5 +168,16 @@ describe('segment-explorer', () => {
145168
framework/ [layout.tsx]
146169
blog/ [layout.tsx, page.tsx]"
147170
`)
171+
expect(await getSegmentExplorerRoute(browser)).toBe(
172+
'/no-layout/framework/blog'
173+
)
174+
})
175+
176+
it('should render route for index page', async () => {
177+
const browser = await next.browser('/')
178+
expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(
179+
`"app/ [layout.tsx, page.tsx]"`
180+
)
181+
expect(await getSegmentExplorerRoute(browser)).toBe('/')
148182
})
149183
})

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,13 @@ describe('Dynamic IO Errors', () => {
237237
at ScrollAndFocusHandler (webpack://<next-src>)
238238
at RenderFromTemplateContext (webpack://<next-src>)
239239
at OuterLayoutRouter (webpack://<next-src>)
240-
332 | */
241-
333 | function InnerLayoutRouter({
242-
> 334 | tree,
240+
333 | */
241+
334 | function InnerLayoutRouter({
242+
> 335 | tree,
243243
| ^
244-
335 | segmentPath,
245-
336 | cacheNode,
246-
337 | url,
244+
336 | segmentPath,
245+
337 | cacheNode,
246+
338 | url,
247247
To get a more detailed stack trace and pinpoint the issue, start the app in development mode by running \`next dev\`, then open "/dynamic-metadata-error-route" in your browser to investigate the error.
248248
Error occurred prerendering page "/dynamic-metadata-error-route". Read more: https://nextjs.org/docs/messages/prerender-error
249249
@@ -739,13 +739,13 @@ describe('Dynamic IO Errors', () => {
739739
at ScrollAndFocusHandler (webpack://<next-src>)
740740
at RenderFromTemplateContext (webpack://<next-src>)
741741
at OuterLayoutRouter (webpack://<next-src>)
742-
332 | */
743-
333 | function InnerLayoutRouter({
744-
> 334 | tree,
742+
333 | */
743+
334 | function InnerLayoutRouter({
744+
> 335 | tree,
745745
| ^
746-
335 | segmentPath,
747-
336 | cacheNode,
748-
337 | url,
746+
336 | segmentPath,
747+
337 | cacheNode,
748+
338 | url,
749749
To get a more detailed stack trace and pinpoint the issue, start the app in development mode by running \`next dev\`, then open "/dynamic-root" in your browser to investigate the error.
750750
Error occurred prerendering page "/dynamic-root". Read more: https://nextjs.org/docs/messages/prerender-error
751751
@@ -1860,13 +1860,13 @@ describe('Dynamic IO Errors', () => {
18601860
at ScrollAndFocusHandler (webpack://<next-src>)
18611861
at RenderFromTemplateContext (<anonymous>)
18621862
at OuterLayoutRouter (webpack://<next-src>)
1863-
332 | */
1864-
333 | function InnerLayoutRouter({
1865-
> 334 | tree,
1863+
333 | */
1864+
334 | function InnerLayoutRouter({
1865+
> 335 | tree,
18661866
| ^
1867-
335 | segmentPath,
1868-
336 | cacheNode,
1869-
337 | url,
1867+
336 | segmentPath,
1868+
337 | cacheNode,
1869+
338 | url,
18701870
To get a more detailed stack trace and pinpoint the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-guarded-clientsync" in your browser to investigate the error.
18711871
Error occurred prerendering page "/sync-attribution/unguarded-async-guarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error
18721872

test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const EXTERNAL = {
1313
const COLLECTOR_PORT = 9001
1414

1515
describe('opentelemetry', () => {
16-
const { next, skipped, isNextDev } = nextTestSetup({
16+
const { next, skipped } = nextTestSetup({
1717
files: __dirname,
1818
skipDeployment: true,
1919
dependencies: require('./package.json').dependencies,
@@ -170,7 +170,7 @@ describe('opentelemetry', () => {
170170
},
171171
{
172172
attributes: {
173-
'next.clientComponentLoadCount': isNextDev ? 8 : 7,
173+
'next.clientComponentLoadCount': 7,
174174
'next.span_type':
175175
'NextNodeServer.clientComponentLoading',
176176
},

test/lib/next-test-utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,12 @@ export async function openDevToolsIndicatorPopover(
962962
}
963963
}
964964

965+
export async function getSegmentExplorerRoute(browser: Playwright) {
966+
return await browser
967+
.elementByCss('.segment-explorer-page-route-bar-path')
968+
.text()
969+
}
970+
965971
export async function getSegmentExplorerContent(browser: Playwright) {
966972
// open the devtool button
967973
await openDevToolsIndicatorPopover(browser)

0 commit comments

Comments
 (0)