Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collapse sequences of call stack frames from React and Next.js in the error overlay #44137

Merged
merged 7 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react'
import type { StackFrame } from 'next/dist/compiled/stacktrace-parser'
import {
getFrameSource,
type OriginalStackFrame,
} from '../../helpers/stack-frame'

export const CallStackFrame: React.FC<{ frame: OriginalStackFrame }> =
function CallStackFrame({ frame }) {
// TODO: ability to expand resolved frames
// TODO: render error or external indicator

const f: StackFrame = frame.originalStackFrame ?? frame.sourceStackFrame
const hasSource = Boolean(frame.originalCodeFrame)

const open = React.useCallback(() => {
if (!hasSource) return

const params = new URLSearchParams()
for (const key in f) {
params.append(key, ((f as any)[key] ?? '').toString())
}

self
.fetch(
`${
process.env.__NEXT_ROUTER_BASEPATH || ''
}/__nextjs_launch-editor?${params.toString()}`
)
.then(
() => {},
() => {
console.error(
'There was an issue opening this code in your editor.'
)
}
)
}, [hasSource, f])

return (
<div data-nextjs-call-stack-frame>
<h6 data-nextjs-frame-expanded={Boolean(frame.expanded)}>
{f.methodName}
</h6>
<div
data-has-source={hasSource ? 'true' : undefined}
tabIndex={hasSource ? 10 : undefined}
role={hasSource ? 'link' : undefined}
onClick={open}
title={hasSource ? 'Click to open in your editor' : undefined}
>
<span>{getFrameSource(f)}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15 3 21 3 21 9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react'
import type { StackFramesGroup } from '../../helpers/group-stack-frames-by-framework'

export function FrameworkIcon({
framework,
}: {
framework: NonNullable<StackFramesGroup['framework']>
}) {
if (framework === 'react') {
return (
<svg
data-nextjs-call-stack-framework-icon="react"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 410 369"
fill="none"
shapeRendering="geometricPrecision"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="5"
>
<path
d="M204.995 224.552C226.56 224.552 244.042 207.07 244.042 185.506C244.042 163.941 226.56 146.459 204.995 146.459C183.43 146.459 165.948 163.941 165.948 185.506C165.948 207.07 183.43 224.552 204.995 224.552Z"
fill="currentColor"
></path>
<path
d="M409.99 184.505C409.99 153.707 381.437 126.667 335.996 108.925C343.342 60.6535 334.19 22.3878 307.492 6.98883C283.649 -6.77511 250.631 -0.0395641 214.512 25.9753C211.316 28.2692 208.143 30.7097 204.97 33.2477C201.822 30.7097 198.65 28.2692 195.477 25.9753C159.359 -0.0395641 126.34 -6.79951 102.497 6.98883C75.8237 22.3878 66.6721 60.6291 74.0422 108.852C28.5529 126.618 0 153.682 0 184.505C0 215.303 28.5528 242.342 73.9934 260.084C66.6477 308.356 75.7993 346.621 102.497 362.02C110.575 366.682 119.727 369 129.684 369C149.085 369 171.61 360.215 195.477 343.034C198.674 340.74 201.847 338.3 205.019 335.762C208.167 338.3 211.34 340.74 214.512 343.034C238.38 360.239 260.905 369 280.306 369C290.263 369 299.415 366.682 307.492 362.02C331.335 348.256 342 316.287 337.534 271.993C337.143 268.089 336.631 264.135 335.996 260.109C381.461 242.367 409.99 215.327 409.99 184.505ZM225.934 41.8136C246.238 27.1955 265.127 19.5814 280.306 19.5814C286.871 19.5814 292.728 20.9968 297.731 23.8765C315.204 33.9798 322.672 62.9475 317.327 102.433C299.756 97.0401 280.306 92.9158 259.392 90.2802C246.872 73.8074 233.597 58.9453 220.003 46.2551C221.98 44.7421 223.957 43.229 225.934 41.8136ZM112.259 23.8765C117.262 20.9968 123.119 19.5814 129.684 19.5814C144.863 19.5814 163.752 27.1711 184.056 41.8136C186.033 43.229 188.01 44.7176 189.986 46.2551C176.393 58.9453 163.142 73.783 150.622 90.2558C129.732 92.8914 110.258 97.0401 92.687 102.409C87.3424 62.9475 94.7857 33.9798 112.259 23.8765ZM19.5233 184.505C19.5233 164.322 40.9014 143.359 77.776 128.253C81.9003 146.141 88.0502 165.054 96.1768 184.456C88.0014 203.881 81.8515 222.819 77.7272 240.732C40.9014 225.626 19.5233 204.687 19.5233 184.505ZM184.056 327.196C154.966 348.134 128.805 354.675 112.259 345.133C94.7857 335.029 87.3181 306.062 92.6626 266.576C110.234 271.969 129.684 276.093 150.598 278.729C163.117 295.202 176.393 310.064 189.986 322.754C188.01 324.292 186.033 325.78 184.056 327.196ZM204.995 310.04C180.591 287.685 157.138 257.815 137.347 223.551C132.051 214.4 121.344 191.396 117 182.489C113.535 190.786 110.112 198.398 107.427 206.5C109.623 210.575 118.092 229.213 120.434 233.288C125.071 241.317 129.928 249.127 134.931 256.692C120.898 254.227 107.915 251.055 96.1035 247.321C102.815 217.011 116.213 182.064 137.347 145.458C142.545 136.453 153.838 116.346 159.5 108C150.568 109.147 143.395 108.767 135 110.5C132.56 114.453 122.777 131.645 120.434 135.721C115.749 143.823 111.454 151.925 107.427 159.978C102.546 146.581 98.8124 133.744 96.1524 121.64C125.755 112.293 162.727 106.411 204.995 106.411C215.562 106.411 237.63 106.197 247.49 106.905C242.048 99.7544 237.38 93.2819 231.694 86.888C227.082 86.7416 209.705 86.888 204.995 86.888C195.672 86.888 186.545 87.2053 177.589 87.7422C186.472 77.1752 195.672 67.5111 204.995 58.9697C229.375 81.3239 252.851 111.195 272.643 145.458C277.841 154.463 289.073 175.426 293.49 184.505C296.98 176.207 300.281 168.64 302.99 160.489C300.793 156.389 291.898 139.747 289.555 135.696C284.918 127.667 280.062 119.858 275.059 112.317C289.092 114.782 302.075 117.954 313.886 121.688C307.175 151.998 293.777 186.945 272.643 223.551C267.445 232.556 252.651 253.178 246.99 261.524C255.922 260.377 265.595 258.663 273.99 256.93C276.43 252.976 287.212 237.364 289.555 233.288C294.216 225.235 298.512 217.182 302.489 209.153C307.224 222.185 310.982 234.997 313.715 247.394C284.138 256.741 247.214 262.598 204.995 262.598C194.428 262.598 169.859 261.208 160 260.5C165.442 267.65 171.304 275.095 176.99 281.489C181.602 281.635 200.285 282.121 204.995 282.121C214.317 282.121 223.444 281.804 232.401 281.267C223.493 291.834 214.317 301.498 204.995 310.04ZM297.731 345.133C281.185 354.699 254.999 348.159 225.934 327.196C223.957 325.78 221.98 324.292 220.003 322.754C233.597 310.064 246.848 295.226 259.367 278.753C280.233 276.118 299.659 271.993 317.205 266.625C317.547 269.089 317.888 271.554 318.132 273.97C321.72 309.649 314.277 335.566 297.731 345.133ZM332.262 240.756C328.065 222.599 321.842 203.686 313.813 184.578C321.988 165.152 328.138 146.215 332.262 128.302C369.088 143.408 390.466 164.322 390.466 184.505C390.466 204.687 369.113 225.626 332.262 240.756Z"
fill="currentColor"
></path>
</svg>
)
}

return (
<svg
data-nextjs-call-stack-framework-icon="next"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 180 180"
fill="none"
>
<mask
id="mask0_408_139"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="180"
height="180"
>
<circle cx="90" cy="90" r="90" fill="black" />
</mask>
<g mask="url(#mask0_408_139)">
<circle
cx="90"
cy="90"
r="87"
fill="black"
stroke="white"
strokeWidth="6"
/>
<path
d="M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z"
fill="url(#paint0_linear_408_139)"
/>
<rect
x="115"
y="54"
width="12"
height="72"
fill="url(#paint1_linear_408_139)"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_408_139"
x1="109"
y1="116.5"
x2="144.5"
y2="160.5"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" />
<stop offset="1" stopColor="white" stopOpacity="0" />
</linearGradient>
<linearGradient
id="paint1_linear_408_139"
x1="121"
y1="54"
x2="120.799"
y2="106.875"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" />
<stop offset="1" stopColor="white" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from 'react'
import type { StackFramesGroup } from '../../helpers/group-stack-frames-by-framework'
import { CallStackFrame } from './CallStackFrame'
import { FrameworkIcon } from './FrameworkIcon'

function FrameworkGroup({
framework,
stackFrames,
all,
}: {
framework: NonNullable<StackFramesGroup['framework']>
stackFrames: StackFramesGroup['stackFrames']
all: boolean
}) {
const [open, setOpen] = React.useState(false)
const toggleOpen = React.useCallback(() => setOpen((v) => !v), [])

return (
<>
<button
data-nextjs-call-stack-framework-button
data-state={open ? 'open' : 'closed'}
onClick={toggleOpen}
tabIndex={10} // Match CallStackFrame tabIndex
>
<svg
data-nextjs-call-stack-chevron-icon
fill="none"
height="20"
width="20"
shapeRendering="geometricPrecision"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
>
<path d="M9 18l6-6-6-6" />
</svg>
<FrameworkIcon framework={framework} />
{framework === 'react' ? 'React' : 'Next.js'}
</button>
{open
? stackFrames.map((frame, index) => (
<CallStackFrame key={`call-stack-${index}-${all}`} frame={frame} />
))
: null}
</>
)
}

export function GroupedStackFrames({
groupedStackFrames,
all,
}: {
groupedStackFrames: StackFramesGroup[]
all: boolean
}) {
return (
<>
{groupedStackFrames.map((stackFramesGroup, groupIndex) => {
// Collapse React and Next.js frames
if (stackFramesGroup.framework) {
return (
<FrameworkGroup
key={`call-stack-framework-group-${groupIndex}-${all}`}
framework={stackFramesGroup.framework}
stackFrames={stackFramesGroup.stackFrames}
all={all}
/>
)
}

return (
// Don't group non React and Next.js frames
stackFramesGroup.stackFrames.map((frame, frameIndex) => (
<CallStackFrame
key={`call-stack-${groupIndex}-${frameIndex}-${all}`}
frame={frame}
/>
))
)
})}
</>
)
}
Loading