mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-22 00:05:51 +00:00
feat: 第一个可以用的 ink 组件抽象 (#158)
This commit is contained in:
134
packages/@ant/ink/src/components/ErrorOverview.tsx
Normal file
134
packages/@ant/ink/src/components/ErrorOverview.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import codeExcerpt, { type CodeExcerpt } from 'code-excerpt'
|
||||
import { readFileSync } from 'fs'
|
||||
import React from 'react'
|
||||
import StackUtils from 'stack-utils'
|
||||
import Box from './Box.js'
|
||||
import Text from './Text.js'
|
||||
|
||||
/* eslint-disable custom-rules/no-process-cwd -- stack trace file:// paths are relative to the real OS cwd, not the virtual cwd */
|
||||
|
||||
// Error's source file is reported as file:///home/user/file.js
|
||||
// This function removes the file://[cwd] part
|
||||
const cleanupPath = (path: string | undefined): string | undefined => {
|
||||
return path?.replace(`file://${process.cwd()}/`, '')
|
||||
}
|
||||
|
||||
let stackUtils: StackUtils | undefined
|
||||
function getStackUtils(): StackUtils {
|
||||
return (stackUtils ??= new StackUtils({
|
||||
cwd: process.cwd(),
|
||||
internals: StackUtils.nodeInternals(),
|
||||
}))
|
||||
}
|
||||
|
||||
/* eslint-enable custom-rules/no-process-cwd */
|
||||
|
||||
type Props = {
|
||||
readonly error: Error
|
||||
}
|
||||
|
||||
export default function ErrorOverview({ error }: Props) {
|
||||
const stack = error.stack ? error.stack.split('\n').slice(1) : undefined
|
||||
const origin = stack ? getStackUtils().parseLine(stack[0]!) : undefined
|
||||
const filePath = cleanupPath(origin?.file)
|
||||
let excerpt: CodeExcerpt[] | undefined
|
||||
let lineWidth = 0
|
||||
|
||||
if (filePath && origin?.line) {
|
||||
try {
|
||||
// eslint-disable-next-line custom-rules/no-sync-fs -- sync render path; error overlay can't go async without suspense restructuring
|
||||
const sourceCode = readFileSync(filePath, 'utf8')
|
||||
excerpt = codeExcerpt(sourceCode, origin.line)
|
||||
|
||||
if (excerpt) {
|
||||
for (const { line } of excerpt) {
|
||||
lineWidth = Math.max(lineWidth, String(line).length)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// file not readable — skip source context
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Box>
|
||||
<Text backgroundColor="ansi:red" color="ansi:white">
|
||||
{' '}
|
||||
ERROR{' '}
|
||||
</Text>
|
||||
|
||||
<Text> {error.message}</Text>
|
||||
</Box>
|
||||
|
||||
{origin && filePath && (
|
||||
<Box marginTop={1}>
|
||||
<Text dim>
|
||||
{filePath}:{origin.line}:{origin.column}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{origin && excerpt && (
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
{excerpt.map(({ line, value }) => (
|
||||
<Box key={line}>
|
||||
<Box width={lineWidth + 1}>
|
||||
<Text
|
||||
dim={line !== origin.line}
|
||||
backgroundColor={
|
||||
line === origin.line ? 'ansi:red' : undefined
|
||||
}
|
||||
color={line === origin.line ? 'ansi:white' : undefined}
|
||||
>
|
||||
{String(line).padStart(lineWidth, ' ')}:
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Text
|
||||
key={line}
|
||||
backgroundColor={line === origin.line ? 'ansi:red' : undefined}
|
||||
color={line === origin.line ? 'ansi:white' : undefined}
|
||||
>
|
||||
{' ' + value}
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{error.stack && (
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
{error.stack
|
||||
.split('\n')
|
||||
.slice(1)
|
||||
.map(line => {
|
||||
const parsedLine = getStackUtils().parseLine(line)
|
||||
|
||||
// If the line from the stack cannot be parsed, we print out the unparsed line.
|
||||
if (!parsedLine) {
|
||||
return (
|
||||
<Box key={line}>
|
||||
<Text dim>- </Text>
|
||||
<Text bold>{line}</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box key={line}>
|
||||
<Text dim>- </Text>
|
||||
<Text bold>{parsedLine.function}</Text>
|
||||
<Text dim>
|
||||
{' '}
|
||||
({cleanupPath(parsedLine.file) ?? ''}:{parsedLine.line}:
|
||||
{parsedLine.column})
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user