Skip to content

[devtools] fix: build error should share the issue content layout #80850

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

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,70 @@
import type { DebugInfo } from '../../../../../shared/types'
import type { ErrorType } from '../../../errors/error-type-label/error-type-label'

import { ErrorMessage } from '../../../errors/error-message/error-message'
import { ErrorOverlayToolbar } from '../../../errors/error-overlay-toolbar/error-overlay-toolbar'
import { ErrorTypeLabel } from '../../../errors/error-type-label/error-type-label'
import { EnvironmentNameLabel } from '../../../errors/environment-name-label/environment-name-label'
import { IssueFeedbackButton } from '../../../errors/error-overlay-toolbar/issue-feedback-button'
import { css } from '../../../../utils/css'

// This behaves like the ErrorOverlayLayout.
export function IssuesTabContentLayout({
error,
errorType,
message,
debugInfo,
children,
errorCode,
environmentName,
}: {
error: Error & { environmentName?: string }
errorType: ErrorType
message: string
debugInfo: DebugInfo
children: React.ReactNode

errorCode?: string | null
environmentName?: string
}) {
return (
<div data-nextjs-devtools-panel-tab-issues-content-layout>
<div className="nextjs-container-errors-header">
<div
className="nextjs__container_errors__error_title"
// allow assertion in tests before error rating is implemented
data-nextjs-error-code={errorCode}
>
<span data-nextjs-error-label-group>
<ErrorTypeLabel errorType={errorType} />
{environmentName && (
<EnvironmentNameLabel environmentName={environmentName} />
)}
</span>
<ErrorOverlayToolbar
error={error}
debugInfo={debugInfo}
// TODO: Move the button inside and remove the feedback on the footer of the error overlay.
feedbackButton={
errorCode && <IssueFeedbackButton errorCode={errorCode} />
}
/>
</div>
<ErrorMessage errorMessage={message} />
</div>
{children}
</div>
)
}

// The components in this file shares the style with the Error Overlay.
export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_LAYOUT_STYLES = css`
[data-nextjs-devtools-panel-tab-issues-content-layout] {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
min-height: 0;
padding: 14px;
}
`
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
import type { OverlayState } from '../../../../shared'
import type { DebugInfo } from '../../../../../shared/types'
import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type'
import type { ErrorType } from '../../../errors/error-type-label/error-type-label'

import { Suspense, useMemo, useState } from 'react'

import {
GenericErrorDescription,
HydrationErrorDescription,
} from '../../../../container/errors'
import { EnvironmentNameLabel } from '../../../errors/environment-name-label/environment-name-label'
import { ErrorMessage } from '../../../errors/error-message/error-message'
import { ErrorOverlayToolbar } from '../../../errors/error-overlay-toolbar/error-overlay-toolbar'
import { ErrorTypeLabel } from '../../../errors/error-type-label/error-type-label'
import { IssueFeedbackButton } from '../../../errors/error-overlay-toolbar/issue-feedback-button'
import { Terminal } from '../../../terminal'
import { HotlinkedText } from '../../../hot-linked-text'
import { PseudoHtmlDiff } from '../../../../container/runtime-error/component-stack-pseudo-html'
Expand All @@ -23,16 +11,22 @@ import { CallStack } from '../../../call-stack/call-stack'
import { NEXTJS_HYDRATION_ERROR_LINK } from '../../../../../shared/react-19-hydration-error'
import { ErrorContentSkeleton } from '../../../../container/runtime-error/error-content-skeleton'
import { css } from '../../../../utils/css'
import { getErrorTextFromBuildErrorMessage } from '../../../../container/build-error'
import { IssuesTabContentLayout } from './issues-tab-content-layout'
import type { DebugInfo } from '../../../../../shared/types'
import type { ErrorType } from '../../../errors/error-type-label/error-type-label'
import { IssuesTabEmptyContent } from './issues-tab-empty-content'

// This consists of the Build Error, Runtime Error, etc.
export function IssuesTabContent({
notes,
buildError,
hydrationWarning,
errorDetails,
activeError,
errorCode,
errorType,
debugInfo,
errorCode,
}: {
notes: string | null
buildError: OverlayState['buildError']
Expand All @@ -41,49 +35,63 @@ export function IssuesTabContent({
hydrationWarning: string | null
notes: string | null
reactOutputComponentDiff: string | null
}
activeError: ReadyRuntimeError
errorCode: string | undefined
errorType: ErrorType
} | null
activeError: ReadyRuntimeError | null
errorType: ErrorType | null
debugInfo: DebugInfo
errorCode: string | null | undefined
}) {
if (buildError) {
return <Terminal content={buildError} />
return <BuildError message={buildError} debugInfo={debugInfo} />
}

const errorMessage = hydrationWarning ? (
<HydrationErrorDescription message={hydrationWarning} />
) : (
<GenericErrorDescription error={activeError.error} />
return (
<ErrorContent
notes={notes}
hydrationWarning={hydrationWarning}
errorDetails={errorDetails}
activeError={activeError}
errorType={errorType}
debugInfo={debugInfo}
errorCode={errorCode}
/>
)
}

function ErrorContent({
notes,
hydrationWarning,
errorDetails,
activeError,
errorType,
debugInfo,
errorCode,
}: {
notes: string | null
hydrationWarning: string | null
errorDetails: {
hydrationWarning: string | null
notes: string | null
reactOutputComponentDiff: string | null
} | null
activeError: ReadyRuntimeError | null
errorType: ErrorType | null
debugInfo: DebugInfo
errorCode: string | null | undefined
}) {
if (!activeError || !errorType) {
return <IssuesTabEmptyContent />
}

return (
<div data-nextjs-devtools-panel-tab-issues-content-container>
<div className="nextjs-container-errors-header">
<div
className="nextjs__container_errors__error_title"
// allow assertion in tests before error rating is implemented
data-nextjs-error-code={errorCode}
>
<span data-nextjs-error-label-group>
<ErrorTypeLabel errorType={errorType} />
{activeError.error.environmentName && (
<EnvironmentNameLabel
environmentName={activeError.error.environmentName}
/>
)}
</span>
<ErrorOverlayToolbar
error={activeError.error}
debugInfo={debugInfo}
// TODO: Move the button inside and remove the feedback on the footer of the error overlay.
feedbackButton={
errorCode && <IssueFeedbackButton errorCode={errorCode} />
}
/>
</div>
<ErrorMessage errorMessage={errorMessage} />
</div>
<IssuesTabContentLayout
error={activeError.error}
errorType={errorType}
message={activeError.error.message}
debugInfo={debugInfo}
errorCode={errorCode}
environmentName={activeError.error.environmentName}
>
<div className="error-overlay-notes-container">
{notes ? (
<>
Expand All @@ -106,15 +114,15 @@ export function IssuesTabContent({
</p>
) : null}
</div>
{errorDetails.reactOutputComponentDiff ? (
{errorDetails?.reactOutputComponentDiff ? (
<PseudoHtmlDiff
reactOutputComponentDiff={errorDetails.reactOutputComponentDiff || ''}
/>
) : null}
<Suspense fallback={<ErrorContentSkeleton />}>
<RuntimeError key={activeError.id.toString()} error={activeError} />
</Suspense>
</div>
</IssuesTabContentLayout>
)
}

Expand Down Expand Up @@ -161,14 +169,29 @@ function RuntimeError({ error }: { error: ReadyRuntimeError }) {
)
}

function BuildError({
message,
debugInfo,
}: {
message: string
debugInfo: DebugInfo
}) {
const error = new Error(message)
const formattedMessage = useMemo(
() => getErrorTextFromBuildErrorMessage(message) || 'Failed to compile',
[message]
)
return (
<IssuesTabContentLayout
errorType={'Build Error'}
error={error}
message={formattedMessage}
debugInfo={debugInfo}
>
<Terminal content={message} />
</IssuesTabContentLayout>
)
}

// The components in this file shares the style with the Error Overlay.
export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_STYLES = css`
[data-nextjs-devtools-panel-tab-issues-content-container] {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
min-height: 0;
padding: 14px;
}
`
export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_STYLES = css``
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Warning } from '../../../../icons/warning'
import { css } from '../../../../utils/css'

export function IssuesTabEmptyContent() {
return (
<div data-nextjs-devtools-panel-tab-issues-empty>
<div data-nextjs-devtools-panel-tab-issues-empty-content>
<div data-nextjs-devtools-panel-tab-issues-empty-icon>
<Warning width={16} height={16} />
</div>
<h3 data-nextjs-devtools-panel-tab-issues-empty-title>
No Issues Found
</h3>
<p data-nextjs-devtools-panel-tab-issues-empty-subtitle>
Issues will appear here when they occur.
</p>
</div>
</div>
)
}

export const DEVTOOLS_PANEL_TAB_ISSUES_EMPTY_CONTENT_STYLES = css`
[data-nextjs-devtools-panel-tab-issues-empty] {
display: flex;
flex: 1;
padding: 12px;
min-height: 0;
}

[data-nextjs-devtools-panel-tab-issues-empty-content] {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
border: 1px dashed var(--color-gray-alpha-500);
border-radius: 4px;
}

[data-nextjs-devtools-panel-tab-issues-empty-icon] {
margin-bottom: 16px;
padding: 8px;
border: 1px solid var(--color-gray-alpha-400);
border-radius: 6px;

background-color: var(--color-background-100);
display: flex;
align-items: center;
justify-content: center;
}

[data-nextjs-devtools-panel-tab-issues-empty-title] {
color: var(--color-gray-1000);
font-size: 16px;
font-weight: 500;
line-height: var(--line-height-20);
}

[data-nextjs-devtools-panel-tab-issues-empty-subtitle] {
color: var(--color-gray-900);
font-size: 14px;
line-height: var(--line-height-21);
}
`
Loading
Loading