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

Replace the group frames with button for ignored frames #72964

Merged
merged 10 commits into from
Nov 28, 2024
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
7 changes: 0 additions & 7 deletions packages/next/src/build/webpack/config/ignore-list.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const styles = css`
border-radius: var(--size-gap-half);
background-color: var(--color-ansi-bg);
color: var(--color-ansi-fg);
margin-bottom: var(--size-gap-double);
}
[data-nextjs-codeframe]::selection,
[data-nextjs-codeframe] *::selection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const CallStackFrame: React.FC<{
</h3>
<div
data-has-source={hasSource ? 'true' : undefined}
data-no-source={hasSource ? undefined : 'true'}
tabIndex={hasSource ? 10 : undefined}
role={hasSource ? 'link' : undefined}
onClick={open}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,78 @@ import * as React from 'react'
import { CodeFrame } from '../../components/CodeFrame'
import type { ReadyRuntimeError } from '../../helpers/get-error-by-type'
import { noop as css } from '../../helpers/noop-template'
import { groupStackFramesByFramework } from '../../helpers/group-stack-frames-by-framework'
import { GroupedStackFrames } from './GroupedStackFrames'
import { CallStackFrame } from './CallStackFrame'

export type RuntimeErrorProps = { error: ReadyRuntimeError }

export function RuntimeError({ error }: RuntimeErrorProps) {
const { frames } = error
const { firstFrame, allLeadingFrames, allCallStackFrames } =
React.useMemo(() => {
const firstFirstPartyFrameIndex = frames.findIndex(
(entry) =>
!entry.ignored &&
Boolean(entry.originalCodeFrame) &&
Boolean(entry.originalStackFrame)
)

return {
firstFrame: frames[firstFirstPartyFrameIndex] ?? null,
allLeadingFrames:
firstFirstPartyFrameIndex < 0
? []
: frames.slice(0, firstFirstPartyFrameIndex),
allCallStackFrames: frames.slice(firstFirstPartyFrameIndex + 1),
}
}, [frames])

const { leadingFramesGroupedByFramework, stackFramesGroupedByFramework } =
React.useMemo(() => {
const leadingFrames = allLeadingFrames.filter((f) => !f.ignored)

return {
stackFramesGroupedByFramework:
groupStackFramesByFramework(allCallStackFrames),

leadingFramesGroupedByFramework:
groupStackFramesByFramework(leadingFrames),
}
}, [allCallStackFrames, allLeadingFrames])
const [isIgnoredExpanded, setIsIgnoredExpanded] = React.useState(false)
const {
firstFrame,
allLeadingFrames,
trailingCallStackFrames,
displayedFramesCount,
} = React.useMemo(() => {
const filteredFrames = error.frames.filter((frame) =>
isIgnoredExpanded ? true : !frame.ignored
)

const firstFirstPartyFrameIndex = filteredFrames.findIndex(
(entry) =>
!entry.ignored &&
Boolean(entry.originalCodeFrame) &&
Boolean(entry.originalStackFrame)
)

return {
displayedFramesCount: filteredFrames.length,
firstFrame: filteredFrames[firstFirstPartyFrameIndex] ?? null,
allLeadingFrames:
firstFirstPartyFrameIndex < 0
? []
: filteredFrames.slice(0, firstFirstPartyFrameIndex),
trailingCallStackFrames: filteredFrames.slice(
firstFirstPartyFrameIndex + 1
),
}
}, [error.frames, isIgnoredExpanded])

return (
<React.Fragment>
{firstFrame ? (
<React.Fragment>
<>
<h2>Source</h2>
<GroupedStackFrames
groupedStackFrames={leadingFramesGroupedByFramework}
/>
{allLeadingFrames.map((frame, frameIndex) => (
<CallStackFrame
key={`call-stack-leading-${frameIndex}`}
frame={frame}
/>
))}
<CodeFrame
stackFrame={firstFrame.originalStackFrame!}
codeFrame={firstFrame.originalCodeFrame!}
/>
</React.Fragment>
</>
) : undefined}

{stackFramesGroupedByFramework.length ? (
<React.Fragment>
<h2>Call Stack</h2>

<GroupedStackFrames
groupedStackFrames={stackFramesGroupedByFramework}
/>
</React.Fragment>
) : undefined}
{trailingCallStackFrames.map((frame, frameIndex) => (
<CallStackFrame
key={`call-stack-leading-${frameIndex}`}
frame={frame}
/>
))}
{
// if the default displayed ignored frames count is equal equal to the total frames count, hide the button
displayedFramesCount === error.frames.length &&
!isIgnoredExpanded ? null : (
<button
data-expand-ignore-button={isIgnoredExpanded}
onClick={() => setIsIgnoredExpanded(!isIgnoredExpanded)}
>
{`${isIgnoredExpanded ? 'Hide' : 'Show'} ignored frames`}
</button>
)
}
</React.Fragment>
)
}
Expand All @@ -75,6 +84,19 @@ export const styles = css`
margin-bottom: var(--size-gap-double);
}

[data-expand-ignore-button]:focus:not(:focus-visible),
[data-expand-ignore-button] {
background: none;
border: none;
color: var(--color-font);
cursor: pointer;
font-size: var(--size-font);
margin: var(--size-gap) 0;
padding: 0;
text-decoration: underline;
outline: none;
}

[data-nextjs-data-runtime-error-copy-button],
[data-nextjs-data-runtime-error-copy-button]:focus:not(:focus-visible) {
position: relative;
Expand Down Expand Up @@ -108,13 +130,13 @@ export const styles = css`
[data-nextjs-call-stack-frame] > h3,
[data-nextjs-component-stack-frame] > h3 {
margin-top: 0;
margin-bottom: var(--size-gap);
margin-bottom: 0;
font-family: var(--font-stack-monospace);
font-size: var(--size-font);
color: #666;
}
[data-nextjs-call-stack-frame] > h3[data-nextjs-frame-expanded='false'] {
color: #666;
display: inline-block;
}
[data-nextjs-call-stack-frame] > div,
[data-nextjs-component-stack-frame] > div {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import type { Project, TurbopackStackFrame } from '../../../../build/swc/types'
import { getSourceMapFromFile } from '../internal/helpers/get-source-map-from-file'
import { findSourceMap } from 'node:module'

function shouldIgnorePath(modulePath: string): boolean {
return (
modulePath.includes('node_modules') ||
// Only relevant for when Next.js is symlinked e.g. in the Next.js monorepo
modulePath.includes('next/dist')
)
}

type IgnorableStackFrame = StackFrame & { ignored: boolean }

const currentSourcesByFile: Map<string, Promise<string | null>> = new Map()
Expand All @@ -33,16 +41,12 @@ export async function batchedTraceSource(
if (!sourceFrame) return

let source = null
let ignored = true
// Don't look up source for node_modules or internals. These can often be large bundled files.
if (
sourceFrame.file &&
!(
sourceFrame.file.includes('node_modules') ||
// isInternal means resource starts with turbopack://[turbopack]
sourceFrame.isInternal
)
) {
const ignored =
shouldIgnorePath(sourceFrame.file) ||
// isInternal means resource starts with turbopack://[turbopack]
!!sourceFrame.isInternal
if (sourceFrame.file && !ignored) {
let sourcePromise = currentSourcesByFile.get(sourceFrame.file)
if (!sourcePromise) {
sourcePromise = project.getSourceForAsset(sourceFrame.file)
Expand All @@ -53,7 +57,6 @@ export async function batchedTraceSource(
currentSourcesByFile.delete(sourceFrame.file!)
}, 100)
}
ignored = false
source = await sourcePromise
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
noContent,
type OriginalStackFrameResponse,
} from './shared'
import { NEXT_PROJECT_ROOT } from '../../../../build/next-dir-paths'
export { getServerError } from '../internal/helpers/node-stack-frames'
export { parseStack } from '../internal/helpers/parse-stack'
export { getSourceMapFromFile }
Expand Down Expand Up @@ -313,7 +312,6 @@ async function getSource(
const modulePath = moduleId.replace(/^(\(.*\)\/?)/, '')

for (const compilation of getCompilations()) {
// TODO: `ignoreList`
const sourceMap = await getSourceMapFromCompilation(moduleId, compilation)
const ignoreList = []
const moduleFilenames = sourceMap?.sources ?? []
Expand Down Expand Up @@ -364,18 +362,8 @@ export function getOverlayMiddleware(options: {
const isEdgeServer = searchParams.get('isEdgeServer') === 'true'
const isAppDirectory = searchParams.get('isAppDirectory') === 'true'
const frame = createStackFrame(searchParams)
const formattedFilePath = formatFrameSourceFile(frame.file)
const filePath = path.join(rootDirectory, formattedFilePath)
const isNextjsSource = filePath.startsWith(NEXT_PROJECT_ROOT)

let sourcePackage = findSourcePackage(frame)
let source: Source | undefined

if (isNextjsSource) {
sourcePackage = 'next'
return json(res, { sourcePackage })
}

try {
source = await getSource(frame.file, {
getCompilations: () => {
Expand Down Expand Up @@ -426,7 +414,6 @@ export function getOverlayMiddleware(options: {
}

if (!source) {
if (sourcePackage) return json(res, { sourcePackage })
return noContent(res)
}

Expand All @@ -438,7 +425,6 @@ export function getOverlayMiddleware(options: {
})

if (!originalStackFrameResponse) {
if (sourcePackage) return json(res, { sourcePackage })
return noContent(res)
}

Expand Down
Loading
Loading