diff --git a/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx b/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx index 01c803cde24a..ada99aa0bd7b 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx @@ -80,6 +80,8 @@ function PageSegmentTreeLayerPresentation({ {folderName && ( {folderName} + {/* hidden slashes for testing snapshots */} + {'/'} )} @@ -151,6 +153,9 @@ export const DEV_TOOLS_INFO_RENDER_FILES_STYLES = css` .segment-explorer-filename--path { margin-right: 8px; } + .segment-explorer-filename--path small { + width: 0; + } .segment-explorer-filename--name { color: var(--color-gray-800); } diff --git a/packages/next/src/next-devtools/dev-overlay/segment-explorer.test.tsx b/packages/next/src/next-devtools/dev-overlay/segment-explorer.test.tsx index 752e501ce37f..0d19bff0f55d 100644 --- a/packages/next/src/next-devtools/dev-overlay/segment-explorer.test.tsx +++ b/packages/next/src/next-devtools/dev-overlay/segment-explorer.test.tsx @@ -75,7 +75,7 @@ describe('Segment Explorer', () => { }) }) - test.failing('remove node in the middle', () => { + test('remove node in the middle', () => { insertSegmentNode({ pagePath: '/a/b/@sidebar/page.js', type: 'page' }) insertSegmentNode({ pagePath: '/a/b/page.js', type: 'page' }) insertSegmentNode({ pagePath: '/a/b/layout.js', type: 'layout' }) diff --git a/packages/next/src/next-devtools/dev-overlay/segment-explorer.ts b/packages/next/src/next-devtools/dev-overlay/segment-explorer.ts index 826149b923c8..a78866eee646 100644 --- a/packages/next/src/next-devtools/dev-overlay/segment-explorer.ts +++ b/packages/next/src/next-devtools/dev-overlay/segment-explorer.ts @@ -55,9 +55,11 @@ const createSegmentTreeStore = (): { const { subscribe, getSnapshot, getServerSnapshot } = createSegmentTreeStore() function createTrie({ - getKey = (k) => k as unknown as string, + getCharacters = (item: Value) => [item] as string[], + compare = (a: Value | undefined, b: Value | undefined) => a === b, }: { - getKey: (k: Value) => string + getCharacters?: (item: Value) => string[] + compare?: (a: Value | undefined, b: Value | undefined) => boolean }): Trie { const root: TrieNode = { value: undefined, @@ -72,8 +74,7 @@ function createTrie({ function insert(value: Value) { let currentNode = root - const key = getKey(value) - const segments = key.split('/') + const segments = getCharacters(value) for (const segment of segments) { if (!currentNode.children[segment]) { @@ -91,8 +92,31 @@ function createTrie({ markUpdated() } - function remove(_: Value) { - // TODO Implement remove functionality + function remove(value: Value) { + let currentNode = root + const segments = getCharacters(value) + const stack: TrieNode[] = [] + let found = true + for (const segment of segments) { + if (!currentNode.children[segment]) { + found = false + break + } + stack.push(currentNode) + currentNode = currentNode.children[segment]! + } + // If the value is not found, skip removal + if (!found || !compare(currentNode.value, value)) { + return + } + currentNode.value = undefined + for (let i = stack.length - 1; i >= 0; i--) { + const parentNode = stack[i] + const segment = segments[i] + if (Object.keys(parentNode.children[segment]!.children).length === 0) { + delete parentNode.children[segment] + } + } markUpdated() } @@ -113,7 +137,11 @@ export type SegmentTrie = Trie export type SegmentTrieNode = TrieNode const trie: SegmentTrie = createTrie({ - getKey: (item) => item.pagePath, + compare: (a, b) => { + if (!a || !b) return false + return a.pagePath === b.pagePath && a.type === b.type + }, + getCharacters: (item) => item.pagePath.split('/'), }) export const insertSegmentNode = trie.insert export const removeSegmentNode = trie.remove diff --git a/test/development/app-dir/segment-explorer/segment-explorer.test.ts b/test/development/app-dir/segment-explorer/segment-explorer.test.ts index 81b29df78768..83e5180ada9a 100644 --- a/test/development/app-dir/segment-explorer/segment-explorer.test.ts +++ b/test/development/app-dir/segment-explorer/segment-explorer.test.ts @@ -26,32 +26,32 @@ describe('segment-explorer', () => { it('should render the segment explorer for parallel routes', async () => { const browser = await next.browser('/parallel-routes') expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(` - "applayout.tsx - parallel-routeslayout.tsx - parallel-routespage.tsx - @barlayout.tsx - @barpage.tsx - @foolayout.tsx - @foopage.tsx" + "app/layout.tsx + parallel-routes/layout.tsx + parallel-routes/page.tsx + @bar/layout.tsx + @bar/page.tsx + @foo/layout.tsx + @foo/page.tsx" `) }) it('should render the segment explorer for nested routes', async () => { const browser = await next.browser('/blog/~/grid') expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(` - "applayout.tsx - (v2)layout.tsx - (team)layout.tsx - (overview)layout.tsx - gridpage.tsx" + "app/layout.tsx + (v2)/layout.tsx + (team)/layout.tsx + (overview)/layout.tsx + grid/page.tsx" `) }) it('should cleanup on soft navigation', async () => { const browser = await next.browser('/soft-navigation/a') expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(` - "applayout.tsx - apage.tsx" + "app/layout.tsx + a/page.tsx" `) await browser.elementByCss('[href="/soft-navigation/b"]').click() @@ -59,11 +59,9 @@ describe('segment-explorer', () => { expect(await browser.elementByCss('body').text()).toContain('Page B') }) - // FIXME: Should no longer contain /soft-navigation/a/page.js expect(await getSegmentExplorerContent(browser)).toMatchInlineSnapshot(` - "applayout.tsx - apage.tsx - bpage.tsx" + "app/layout.tsx + b/page.tsx" `) }) })